AtCoder Beginner Contest 216

AtCoder Beginner Contest 216

掉大分场,H 涉及 LGV 引理所以锅了。

\(\mathcal{A\sim D}\)

A 简单判断,B 简单模拟,C 简单贪心,D 简单模拟。

\(\mathcal E\)

给定初始序列 \(A\),每次可以选择一个 \(a_i\),将总价值加上它,然后令 \(a_i-1\)。

总共进行 \(m\) 轮,求最终得到的最大价值数。

可以先二分找到最大的,能够将所有 \(>x\) 的 \(a_i\) 都直接减到 \(x-1\) 的 \(x\)。

然后直接用大根堆模拟,来暴力选取后面的数,因为去掉上面的数之后,至多只剩下 \(n\) 轮选择,所以复杂度是对的。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int N = 1e5 + 10;

int n, k, a[N];
priority_queue<LL> q;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

LL Get() {
	LL l = 1, r = 0;
	for(int i = 1; i <= n; i ++) r = max(r, (LL)a[i]);
	r ++;
	while(l < r) {
		LL mid = (l + r) >> 1, num = 0;
		for(int i = 1; i <= n; i ++)
			if(a[i] >= mid) num += a[i] - mid + 1;
		if(num <= k) r = mid; else l = mid + 1;
	}
	return l;
}

int main() {
	n = read(), k = read();
	for(int i = 1; i <= n; i ++) a[i] = read();
	LL lim = Get(), ans = 0, num = 0;
	for(int i = 1; i <= n; i ++)
		if(a[i] >= lim) {
			num += a[i] - lim + 1;
			ans += (a[i] - lim + 1) * (a[i] + lim) / 2LL;
			a[i] = lim - 1;
		}
	for(int i = 1; i <= n; i ++) q.push(a[i]);
	for(LL i = num + 1; i <= k; i ++) {
		LL now = q.top(); q.pop();
		if(now <= 0) break;
		ans += now, q.push(now - 1);
	}
	printf("%lld\n", ans);
	return 0;
}

\(\mathcal F\)

给定 \(n\) 对 \((A_i,B_i)\),求集合 \(S\) 的方案数,集合 \(S\) 需满足:

\[\max\limits_{i\in S} A_i\geq \sum\limits_{i\in S} B_i \]

考场正解 + 降智/kk

将 \(A\) 排序,每次枚举最大的 \(A\),然后在前面的集合中,选择 \(\sum B\leq A_i-B_i\) 的方案数。

相当于简单的 01 背包,然后考场思路直接降序 \(A\),然后做背包,时间复杂度 \(O(n^3)\),优化的话可以直接退背包。

实际上直接顺序 \(A\),然后每次加入即可,QwQ。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 5010;
const LL P = 998244353;

int n, sum[N]; LL f[N];
struct Node{int a, b;} p[N];

int read() {
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(Node x, Node y) {return x.a < y.a;}

int main() {
	n = read();
	for(int i = 1; i <= n; i ++) p[i].a = read();
	for(int i = 1; i <= n; i ++) p[i].b = read();
	sort(p + 1, p + n + 1, cmp);
	LL ans = 0; f[0] = 1;
	for(int i = 1; i <= n; i ++) {
		int v = p[i].b;
		for(int j = 0; j <= p[i].a - p[i].b; j ++) ans = (ans + f[j]) % P;
		for(int j = 5000; j >= v; j --) f[j] = (f[j] + f[j - v]) % P;
	}
	printf("%lld\n", ans);
	return 0;
}

\(\mathcal G\)

要求构造长度为 \(n\) 的 01 串,有 \(m\) 个形如 \((l,r,x)\) 要求,表示区间 \([l,r]\) 中有 \(\geq x\) 个 \(1\)。

要求构造方案的 \(1\) 的总数最小,求任意一种方案即可,数据保证有解。

差分约束版题?其实不用。

直接贪心按右端点排序,然后尽量填右边即可,结合树状数组 + 并查集,不难做到 \(O(n\log n)\)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 2e5 + 10;
int n, m, fa[N], ans[N], c[N];
struct Node{int l, r, x;} a[N];

int read(){
	int x = 0, f = 1; char c = getchar();
	while(c < '0' || c > '9') f = (c == '-') ? - 1 : 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
	return x * f;
}

bool cmp(Node a, Node b) {return a.r < b.r;}

int Get(int x) {
	if(x == fa[x]) return x;
	return fa[x] = Get(fa[x]);
}

void Add(int x) {for(; x <= n; x += x & -x) c[x] ++;}
int Ask(int x) {int sum = 0; for(; x; x -= x & -x) sum += c[x]; return sum;}

int main(){
	n = read(), m = read();
	for(int i = 1; i <= n; i ++) fa[i] = i;
	for(int i = 1; i <= m; i ++)
		a[i].l = read(), a[i].r = read(), a[i].x = read();
	sort(a + 1, a + m + 1, cmp);
	for(int i = 1; i <= m; i ++) {
		int lim = a[i].x - (Ask(a[i].r) - Ask(a[i].l - 1));
		for(int j = 1; j <= lim; j ++) {
			int u = Get(a[i].r);
			Add(u), ans[u] = 1;
			fa[u] = Get(u - 1);
		}
	}
	for(int i = 1; i <= n; i ++) printf("%d ", ans[i]);
	puts("");
	return 0;
}
上一篇:知识点:快读函数


下一篇:两个水题和两个大水题的故事