掉大分场,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;
}