一万四参赛,赛时(VP)排名 60

A. Max Plus Size

一定选择最大值,若有多个最大值,优先选在奇数位置的最大值,后间隔涂上红色。

#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int T, n, a[N];

int main() {
	scanf("%d", &T);
	while(T --) {
		scanf("%d", &n);
		int mx = 0;
		for(int i = 1; i <= n; i ++)
			scanf("%d", &a[i]), mx = max(mx, a[i]);
		if(n & 1) {
			int flag = 0;
			for(int i = 1; i <= n; i ++)
				if(mx == a[i] && (i & 1)) flag = true;
			printf("%d\n", mx + n / 2 + flag);
		}
		else printf("%d\n", mx + n / 2);
	}
	return 0;
}

B. All Pairs Segments

对于相邻两个数之间的区间,所有数被覆盖次数相同,且与左右节点数相关,列式子计算即可。

用 map[x] 记录恰好被 x 条线段覆盖的数的数量。

针对询问输出即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int T, n, m, a[N];

int main() {
	scanf("%d", &T);
	while(T --) {
		map<long long, int> mp;
		scanf("%d%d", &n, &m);
		for(int i = 1; i <= n; i ++)
			scanf("%d", &a[i]);
		for(int i = 2; i <= n; i ++) {
			long long s = 1ll * (i - 1) * (n - i + 1);
			mp[s] += a[i] - a[i - 1] - 1;
		}
		for(int i = 1; i <= n; i ++) {
			long long s = 1ll * (i - 1) * (n - i);
			s += n - 1;
			mp[s] += 1;
		}
		while(m --) {
			long long k;
			scanf("%lld", &k);
			printf("%d ", mp[k]);
		}
		printf("\n");
	}
	return 0;
}

C. Cards Partition

每个抽屉里面不能有相同的卡片,因此每个抽屉最多 n 张卡片。另一方面,最少得有 max(a[i]) 个抽屉。

当 k 较大时,直接输出 min{n, sum / max(a[i])} 。

当 k 较小时,从 sum / max(a[i]) 开始枚举每个抽屉中卡片的数量。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long T, n, k, a[N];

int main() {
	scanf("%lld", &T);
	while(T --) {
		scanf("%lld%lld", &n, &k);
		long long sum = 0, mx = 0;
		for(int i = 1; i <= n; i ++)
			scanf("%lld", &a[i]), sum += a[i], mx = max(a[i], mx);
		if(sum / mx != (sum + k) / mx)
			printf("%d\n", min((sum + k) / mx, n));
		else {
			for(int i = sum / mx; i >= 1; i --) {
				int t = sum % i;
				if(t == 0) t = i;
				if(i - t <= k) {
					printf("%d\n", i);
					break;
				}
			}
		}
	}
	return 0;
}

D. Speedbreaker

显然,征服区间时刻是连续。

对于每一时间点 i ,找到最左端小于等于 i 的 a[l] ,最右端小于等于 i 的 a[r] 。在 i 时刻 [l,r] 内城市都得被征服。

因此出发点只能是在区间 [r - i + 1,l + i - 1] 内。

求出每个区间求并即可。

注意:如第 i 个时间点导出的起始点区间长度小于 i ,则无解。

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int T, n, s[N], l[N], r[N];

int main() {
	scanf("%d", &T);
	while(T --) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i ++) s[i] = 0, l[i] = 0;
		for(int i = 1, x; i <= n; i ++) {
			scanf("%d", &x);
			if(!l[x]) l[x] = i;
			r[x] = i;
		}
		int L = 0, R = 0;
		for(int i = 1; i <= n; i ++)
			if(l[i]) {
				if(!L) L = l[i], R = r[i];
				else L = min(l[i], L), R = max(R, r[i]);
				l[i] = L, r[i] = R;
			}
		int cnt = 0;
		bool flag = true;
		for(int i = 1; i <= n; i ++)
			if(l[i]) {
				int L = max(1, r[i] - i + 1);
				int R = min(n, l[i] + i - 1);
				if(R - L + 1 < i) flag = false;
				else s[L] ++, s[R + 1] --;
				cnt ++;
			}
		int t = 0, ans = 0;
		for(int i = 1; i <= n; i ++) {
			t += s[i];
			if(t == cnt) ans ++;
		}
		if(!flag) ans = 0;
		printf("%d\n", ans);
	}
	return 0;
}

E. Tree Pruning

选定一个深度,深度大于的节点需要全部剪掉,深度小于的子节点及枝桠需要剪掉。

因此从 1 开始枚举深度,深度每增加 1 ,所有叶子节点伸长一个单位(或者分叉)。没有伸长的叶子节点回溯修剪掉多余的枝桠。

每次更新答案,取最小值即可。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int T, n, ans, cnt[N], fa[N];
vector<int> g[N];

void get_ans(int rt) {
	queue<int> q;
	q.push(rt); cnt[rt] = 1; fa[rt] = 0;
	int res = n - 1;
	while(q.size()) {
		int t = q.size();
		for(int i = 0; i < t; i ++) {
			int x = q.front(); q.pop();
			for(auto y: g[x]) {
				if(y == fa[x]) continue;
				q.push(y);
				cnt[x] ++;
				fa[y] = x;
				res --;
			}
			if(!cnt[x]) {
				res ++;
				while(-- cnt[fa[x]] == 0) res ++, x = fa[x];
			}
		}
		ans = min(ans, res);
	}
}

int main() {
	scanf("%d", &T);
	while(T --) {
		scanf("%d", &n);
		for(int i = 1; i <= n; i ++)
			g[i].clear(), cnt[i] = 0;
		for(int i = 2, u, v; i <= n; i ++) {
			scanf("%d%d", &u, &v);
			g[u].push_back(v);
			g[v].push_back(u);
		}
		ans = n;
		get_ans(1);
		printf("%d\n", ans);
	}
	return 0;
}