Codeforces Round #722 (Div. 2)

比賽地址c++

A(水題)

題目連接
數組

題意:
給出一組序列,每次能夠將大於當前數組平均數的數刪去,問最多能夠刪去多少個數spa

解析:
很是顯然,因爲最小值會一直拉低平均值使得其一直小於最大值,因此只有最小值會被保留code

#include<bits/stdc++.h>
using namespace std;

int dat[10005];

int main() {
	int T, n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		int mn=0x3f3f3f3f;
		for (int i = 0; i < n; ++i)
			scanf("%d", &dat[i]), mn=min(mn,dat[i]);
		int t = n;
		for (int i = 0; i < n; ++i)
			if (dat[i] == mn) --t;
		printf("%d\n", t);
	}
}

B(貪心)

題目連接
get

題意:
定義數組\(b\)中任意兩元素的差的絕對值小於等於數組內的最大值,則稱這個數組是strange的。如今給出數組\(a\),求\(a\)的子序列構成strange數組的最大長度it

解析:class

  1. 若是\(a\)中元素非正數,毫無疑問都是符合\(strange\)條件的,如今問題就轉換成了對於正數部分該如何處理。顯然若是某個正數的添加使得必需要清除負數,這樣是不划算的(由於這樣使得MAX變大了,但數組長度沒變),並且在成功添加的狀況下,正數越大約不划算
  2. 因而貪心策略就是,把全部負數收入囊中,並維護一個相鄰的最小差的絕對值,若是沒法插入就輸出答案,不然添加
#include<bits/stdc++.h>

using namespace std;

int dat[100005];

int main() {
	int T, n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 0; i < n; ++i)
			scanf("%d", &dat[i]);
		sort(dat, dat + n);
		int i;
		int mn = INT32_MAX;
		int ans = 0;
		for (i = 0; i < n; ++i) {
			if (i) mn = min(mn, dat[i] - dat[i - 1]);
			if (dat[i] > mn) break;
			++ans;
		}
		printf("%d\n", ans);
	}
}

C(樹上dp)

題目連接
⭐⭐test

題意:
給出一個樹,以及每一個節點之間的取值範圍,如今對每一個節點取某個值,使得相鄰節點值的差絕對值的和最大im

解析:統計

  1. 不論每一個節點何種取值,不難發現,只有取得某個節點的端點值時,纔有可能得到最大的絕對值的和
  2. 定義狀態\(dp[i][0]\),表明第\(i\)個節點選擇左端點時的最大值,以及\(dp[i][1]\),表明第\(i\)個節點選擇右端點時的最大值,那不可貴到狀態轉移式

\[\begin{cases} dp[u][0]=\max(dp[v][0]+|l[u]-l[v]|,dp[v][1]+|l[u]-r[v]|) \\ dp[u][1]=\max(dp[v][0]+|r[u]-l[v]|,dp[v][1]+|r[u]-r[v]|) \end{cases} \]

#include<bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5 + 5;
vector<int> e[maxn];
int l[maxn], r[maxn];
long long dp[maxn][2];
 
void dfs(int fa, int cur) {
	for (auto& i : e[cur]) {
		if (i == fa) continue;
		dfs(cur, i);
		dp[cur][0] += max(dp[i][0] + abs(l[cur] - l[i]), dp[i][1] + abs(l[cur] - r[i]));
		dp[cur][1] += max(dp[i][0] + abs(r[cur] - l[i]), dp[i][1] + abs(r[cur] - r[i]));
	}
}
 
int main() {
	int T, n,a,b;
	scanf("%d", &T);
	while (T--) {
		memset(dp, 0, sizeof(dp));
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) {
			scanf("%d%d", &l[i], &r[i]);
			e[i].clear();
		}
		for (int i = 1; i < n; ++i)
			scanf("%d%d", &a, &b), e[a].push_back(b), e[b].push_back(a);
		dfs(0, 1);
		printf("%lld\n", max(dp[1][0], dp[1][1]));
	}
}

D(dp)

題目連接
⭐⭐⭐

題意:
在一個長度爲\(2n\)的軸上,選擇\(n\)個點對,保證任意兩個點對的距離都相等,或者一個點對被包含在另外一個點對中,詢問合法的有多少種點對選擇方式

解析:

  1. 當選擇點對左邊選擇第一個點時,考慮右邊選擇的點的位置大於\(n\)時的狀況,例如選擇第\(2n\)個點時,那就還有\(n-1\)個點對,同理選擇第\(2n-1\)個點時,對於左端第二個點也能夠選擇一個相同長度的點對,這樣就還有\(n-2\)個點對……,因此這部分的貢獻值爲\(\sum\limits_{i=0}^{n-1}dp[i]\)
  2. 當選擇的點的位置小於等於\(n\)時,假定這個點以前的點爲\(x\),則構成\((1,x+1),(2,x+2),\dots(x,2x)\)的點對集小總體,也就是說這一個總體每一個點對長度都爲\(x\),且總體長度爲\(2x\),那隻要保證\(2x|2n\)便可,因此這部分貢獻是\(\sum_{i|n} dp[i]\)
  3. 對於第一部分可使用前綴和進行統計,第二部分能夠對每一個因子統計貢獻計算得到
#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 5;
long long dp[maxn];
const long long mod = 998244353;

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
		for (int j = 2 * i; j <= n; j += i)
			++dp[j];
	dp[0] = 1;
	for (int i = 1; i <= n; ++i)
		dp[i] = (2 * dp[i - 1] + dp[i]) % mod;
	printf("%lld", (dp[n] - dp[n - 1] + mod) % mod);
}

E(dfs+樹+貪心)

題目連接
⭐⭐⭐⭐

題意:
給出兩個樹,找到最大的點集使得在一號樹中在點集內的全部點要求在一條鏈上,在二號樹中在點集內的任意兩個點不能一個點是另外一個點的祖宗

解析:

  1. 首先考慮對二號樹進行\(dfs\),求得每一個節點對應的子樹區間範圍\((l,r)\)
  2. 考慮直接對一號樹進行\(dfs\),在\(dfs\)的過程當中維護當前已經探索到的點集,這樣能夠保證當前點集在一條鏈上,那麼如今對於一個新探索到的點,考慮幾種狀況來判斷是否能夠加入點集
    • 若是新的點是點集中某個點的子樹內的一點,那麼能夠貪心的用新點代替舊點,由於這樣的狀況下點集大小不變,可是所覆蓋的點的範圍減小了
    • 若是新的點是點集中某個點的祖宗,則與第一種狀況一樣分析,這時候不加這個點是最貪心的
    • 若是這個點和點集內全部點沒有交集,則加入這個點
  3. 問題轉化成了如何判斷點與點的親緣關係,能夠經過1中計算的\((l,r)\),給出某個點\(u\),以及他的子樹中的一點\(v\),必定有\(l[u]\le l[v],r[u]\ge r[v]\),而且很容易得知,對於一個樹而言,全部的節點的\((l,r)\)只有包含關係與不相關兩種狀況,因此當得知\(l[a]\le l[b],r[a]\le r[b]\)時,確定能得出\(a,b\)不相關
#include<bits/stdc++.h>

using namespace std;

const int maxn = 3e5 + 5;
vector<int> e1[maxn], e2[maxn];
int l[maxn], r[maxn];
int cnt,ans;
typedef pair<int, int> P;
set<P> s;

void dfs2(int cur) {
	l[cur] = ++cnt;
	for (auto& i : e2[cur]) dfs2(i);
	r[cur] = cnt;
}

int insert(int x) {
	auto i = s.lower_bound(P(l[x],x));
	if (i != s.end() && r[i->second] <= r[x]) return 0;
	if (i == s.begin()) return -1;
	--i;
	int t = i->second;
	if (r[x] > r[t]) return -1;
	s.erase(i);
	return t;
}

void dfs1(int cur) {
	int res = insert(cur);
	if (res) s.insert(P(l[cur], cur));
	ans = max(ans, (int)s.size());
	for (auto& i : e1[cur]) dfs1(i);
	if (res) {
		s.erase(P(l[cur], cur));
		if (~res) s.insert(P(l[res], res));
	}
}


int main() {
	int T, n, t;
	scanf("%d", &T);
	while (T--) {
		cnt =ans= 0;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i) e1[i].clear(), e2[i].clear();
		for (int i = 2; i <= n; ++i)
			scanf("%d", &t), e1[t].push_back(i);
		for (int i = 2; i <= n; ++i)
			scanf("%d", &t), e2[t].push_back(i);
		dfs2(1);
		dfs1(1);
		printf("%d\n", ans);
	}
}
相關文章
相關標籤/搜索