Codeforces Round #782 (Div. 2) VP 记录
赛时 4 题,E 是赛后补的 /kk
A. Red Versus Blue
题目保证了 ,那么也就是说 把整个序列分成了 份,然后每份中填入尽可能少的 。
最优的方式显然是尽可能去均匀分配,每份填入 个 ,如果有剩下的 没填,那每份多填一个即可。
void Main() {
n = read(), R = read(), B = read();
int p = R / (B + 1), q = R % (B + 1);
for(int j = 1; j <= p + (1 <= q); ++j) cout << "R";
for(int i = 1; i <= B; ++i) {
cout << "B";
for(int j = 1; j <= p + (i + 1 <= q); ++j) cout << "R";
}
puts("");
}
signed main() {
T = read();
while(T--) Main();
return 0;
}
B. Bit Flipping
反向考虑,每个位置都会被异或 次,每次选中一个位置就是让它少异或一次。
从前向后贪心看看能不能让尽可能多的位置异或成 即可,多余的操作次数全扔到最后一位。
void Main() {
n = read(), K = read(); int lst = K;
for(int i = 1; i <= n; ++i) scanf("%1d", &a[i]), cnt[i] = 0;
for(int i = 1; i <= n; ++i) {
if((K & 1) && a[i] && lst) lst--, cnt[i] = 1;
if(!(K & 1) && !a[i] && lst) lst--, cnt[i] = 1;
}
if(lst) cnt[n] += lst;
for(int i = 1; i <= n; ++i) printf("%d", a[i] ^ ((K - cnt[i]) & 1)); puts("");
for(int i = 1; i <= n; ++i) printf("%d ", cnt[i]); puts("");
}
signed main() {
T = read();
while(T--) Main();
return 0;
}
C. Line Empire
第一眼可以设一个 表示基地在第 个位置,并且走完了前 个位置的最小花费。
不过这样转移是 的,显然过不了。
考虑这个题目自身的性质,发现如果基地最后走到了第 个位置,那么在占领第 个位置之前都可以把基地移动到第 个位置然后再去占领第 个位置,而对于第 个之后的位置都只能从第 个位置出发去占领。也就是说如果我们确定了最后基地最后走到了哪个位置,那么整个过程的最优方案我们也是确定的。
所以我们只需要枚举基地最后在哪个位置,然后通过预处理一些需要的东西就可以直接 算出贡献了。
void Main() {
n = read(), A = read(), B = read(), sum[n + 1] = 0;
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = n; i >= 1; --i) sum[i] = sum[i + 1] + a[i];
int ans = INF;
for(int i = 0; i <= n; ++i) ans = min(ans, A * a[i] + B * a[i] + B * (sum[i + 1] - a[i] * (n - i)));
cout << ans << "\n";
}
signed main() {
T = read();
while(T--) Main();
return 0;
}
D. Reverse Sort Sum
可以从前往后考虑这个位能不能填 。
如果之前填了 个 ,当前考虑到第 位。
那么在这里填 之后,会对 这段区间有一个 的贡献,并且在这之前这个 还会对 这个位置产生 次贡献。
区间加减操作直接上线段树。
假设这里填 ,如果减去它产生的贡献后发现最小值出现了负数,说明这里应该填 ,否则说明这里可以填 。
正确性大概就是每个位置 的贡献都是越来越靠后的?就是说后面的 不会对前面的造成贡献,所以可以从前向后确定。
namespace Seg {
#define lson i << 1
#define rson i << 1 | 1
int Min[MAXN << 2], lazy[MAXN << 2];
void Push_up(int i) { Min[i] = min(Min[lson], Min[rson]); }
void Build(int i, int l, int r) {
lazy[i] = Min[i] = 0;
if(l == r) return Min[i] = a[l], void();
int mid = (l + r) >> 1;
Build(lson, l, mid), Build(rson, mid + 1, r);
Push_up(i);
}
void Push_down(int i) {
if(!lazy[i]) return ;
lazy[lson] += lazy[i], lazy[rson] += lazy[i];
Min[lson] -= lazy[i], Min[rson] -= lazy[i];
lazy[i] = 0;
}
void Modify(int i, int l, int r, int L, int R, int val) {
if(L <= l && r <= R) return lazy[i] += val, Min[i] -= val, void();
Push_down(i); int mid = (l + r) >> 1;
if(mid >= L) Modify(lson, l, mid, L, R, val);
if(mid < R) Modify(rson, mid + 1, r, L, R, val);
Push_up(i);
}
int Query(int i, int l, int r, int L, int R) {
if(L <= l && r <= R) return Min[i];
Push_down(i); int mid = (l + r) >> 1, ans = INF;
if(mid >= L) ans = min(ans, Query(lson, l, mid, L, R));
if(mid < R) ans = min(ans, Query(rson, mid + 1, r, L, R));
return ans;
}
}
void Main() {
n = read();
for(int i = 1; i <= n; ++i) a[i] = read();
Seg::Build(1, 1, n);
int lst = 0;
for(int i = 1; i <= n; ++i) {
int L = i - lst, R = i - lst + n - i;
Seg::Modify(1, 1, n, L, R, 1);
Seg::Modify(1, 1, n, i, i, i - 1);
int p = Seg::Query(1, 1, n, L, R);
int q = Seg::Query(1, 1, n, i, i);
if(p >= 0 && q >= 0) {
b[i] = 1, lst ++;
} else {
b[i] = 0;
Seg::Modify(1, 1, n, L, R, - 1);
Seg::Modify(1, 1, n, i, i, - i + 1);
}
}
for(int i = 1; i <= n; ++i) cout << b[i] << " "; puts("");
}
signed main() {
T = read();
while(T--) Main();
return 0;
}
E. AND-MEX Walk
胡乱手模可以发现答案只能是 ,因为做 前缀和的话 不能同时出现。
看到位运算想到拆位。因为点可以重复经过,所以可以往联通块的方向上去想。
考虑答案为 的情况,就是存在一条 的路径,这个路径上的所有边至少有一位全部都为 。
那么我们根据边的权值,对于每一位用一个并查集,如果边 的 的这一位为 ,那么可以在并查集中将 合并。
那答案为 的情况就变成了判断是否存在一位的并查集中 属于同一个连通块。
考虑答案为 的情况。首先要建立在答案不是 之前。
然后,我们要在 前缀和变为 之前不能出现让其 ,我们可以在先保证二进制中的某一位都为 之前,先把二进制中的第一位的 消掉。
那么,对于所有边权为偶数的边,对每一位所在的连通块打上标记。
如果存在一位中 这个点的联通块被打了标记,那么说明上面的条件可以做到,也就是说答案为 。
否则答案为 。
struct node { int u, v, w; }e[MAXN];
int n, m, Q;
struct Graph {
int fa[MAXN];
bool vis[MAXN];
void Init() { for(int i = 1; i <= n; ++i) fa[i] = i; }
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void Add(int x, int y) {
int uf = find(x), vf = find(y);
if(uf != vf) fa[uf] = vf;
}
bool Query(int x, int y) { return find(x) == find(y); }
}G[31];
signed main() {
n = read(), m = read();
for(int i = 0; i <= 30; ++i) G[i].Init();
for(int i = 1, u, v, w; i <= m; ++i) {
e[i].u = read(), e[i].v = read(), e[i].w = read();
for(int j = 0; j <= 30; ++j) {
if((e[i].w >> j) & 1) {
G[j].Add(e[i].u, e[i].v);
}
}
}
for(int i = 1; i <= m; ++i) {
if(e[i].w & 1) continue;
for(int j = 1; j <= 30; ++j) {
G[j].vis[G[j].find(e[i].u)] = true;
G[j].vis[G[j].find(e[i].v)] = true;
}
}
Q = read();
for(int i = 1, u, v; i <= Q; ++i) {
u = read(), v = read();
bool flag = false;
for(int j = 0; j <= 30; ++j) flag |= (G[j].Query(u, v));
if(flag) { puts("0"); continue; }
for(int j = 1; j <= 30; ++j) flag |= G[j].vis[G[j].find(u)];
flag ? puts("1") : puts("2");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具