constint N = 50010; int n, k, p[N], d[N]; intfind(int x){ if (x != p[x]) { int root = find(p[x]); d[x] += d[p[x]]; p[x] = root; } return p[x]; }
intmain(){ cin >> n >> k; for (int i = 1; i <= n; i++) p[i] = i; int fake = 0; while (k--) { int t, x, y; cin >> t >> x >> y; if (x > n || y > n) { fake++; continue; } int px = find(x), py = find(y); if (t == 1) { if (px == py) { if ((d[x] - d[y]) % 3 != 0) fake++; } else p[px] = py, d[px] = d[y] - d[x]; } else { if (px == py) { if ((d[x] - d[y] - 1) % 3 != 0) fake++; } else p[px] = py, d[px] = d[y]-d[x]+1; } } cout << fake << endl; return0; }
白雪皑皑
现在有 n 片雪花排成一列。 pty 要对雪花进行 m 次染色操作,第 i 次染色操作中,把第 ((i×p+q)modn)+1 片雪花和第 ((i×q+p)modn)+1 片雪花之间的雪花(包括端点)染成颜色 i。其中 p,q 是给定的两个正整数。他想知道最后 n 片雪花被染成了什么颜色。没有被染色输出 0。
voidmodify(int u, int l, int r, int v){ if (l <= tr[u].l && tr[u].r <= r) return tr[u].val = v, void(); pushdown(u); int mid = (tr[u].l + tr[u].r) >> 1; if (l <= mid) modify(u<<1, l, r, v); if (mid+1 <= r) modify(u<<1|1, l, r, v); }
intquery(int u, int p){ if (tr[u].l == tr[u].r) return tr[u].val; pushdown(u); int mid = (tr[u].l + tr[u].r) >> 1; if (p <= mid) returnquery(u<<1, p); elsereturnquery(u<<1|1, p); }
voidbuild(int u, int l, int r){ tr[u].l = l, tr[u].r = r; if (l == r) return; int mid = (l+r) >> 1; build(u<<1, l, mid), build(u<<1|1, mid+1, r); }
intmain(){ scanf("%d%d%d%d", &n, &m, &p, &q);
build(1, 1, n); for (int i = 1; i <= m; i++) { int l = (1LL*i*p+q) % n + 1, r = (1LL*i*q+p) % n + 1; if (l > r) swap(l, r); modify(1, l, r, i); } for (int i = 1; i <= n; i++) { printf("%d\n", query(1, i)); } return0; }
for (int i = m; i; i--) { int l = (i*p+q) % n + 1, r = (i*q+p) % n + 1; if (l > r) swap(l, r); for (int j = find(l); j <= r; j = fa[j]) { color[j] = i; fa[j] = find(j+1); } }
for (int i = 1; i <= n; i++) { printf("%d\n", color[i]); } return0; }
[CEOI1999] Parity Game
Alice 和 Bob 在玩一个游戏:他写一个由 0 和 1 组成的序列。Alice 选其中的一段(比如第 3 位到第 5 位),问他这段里面有奇数个 1 还是偶数个 1。Bob 回答你的问题,然后 Alice 继续问。Alice 要检查 Bob 的答案,指出在 Bob 的第几个回答一定有问题。有问题的意思就是存在一个 01 序列满足这个回答前的所有回答,而且不存在序列满足这个回答前的所有回答及这个回答。
输入格式
第 1 行一个整数 n,是这个 01 序列的长度。
第 2 行一个整数 m,是问题和答案的个数。
第 3 行开始是问题和答案,每行先有两个整数,表示你询问的段的开始位置和结束位置。然后是 Bob 的回答。odd表示有奇数个 1,even 表示有偶数个 1。
intmain(){ scanf("%d%d", &n, &m); for (int i = 1, a, b, c; i <= m; i++) { scanf("%d%d%d", &a, &b, &c); e[i] = {a, b, c}; } scanf("%d%d", &S, &T); sort(e+1, e+1+m);
Fraction ans = {INF, 1}; for (int i = 1; i <= m; i++) { for (int i = 1; i <= n; i++) p[i] = i;
for (int j = i; j <= m; j++) { int a = e[j].a, b = e[j].b; p[find(a)] = find(b); if (find(S) == find(T)) { ans = min(ans, Fraction{e[j].c, e[i].c}); break; } } }
ans.minimal(); if (ans.a == INF) puts("IMPOSSIBLE"); else { if (ans.b == 1) printf("%d\n", ans.a); elseprintf("%d/%d\n", ans.a, ans.b); } return0; }
染色
给定 n 点的树和 q 条路径,需要保证同一路径上的颜色相同,只能染成黑或白,求黑白点数颜色差的所有可能,n,q≤3×105。
voidmerge(int a, int b){ int pa = find(a), pb = find(b); if (pa == pb) return; sz[pb] += sz[pa]; p[pa] = pb; }
voidadd(int a, int b){ e[idx] = {b, h[a]}, h[a] = idx++; }
voiddfs(int u, int fath){ fa[u][0] = fath, dep[u] = dep[fath] + 1; for (int k = 1; k < K; k++) fa[u][k] = fa[fa[u][k-1]][k-1];
for (int i = h[u]; i; i = e[i].nxt) { int to = e[i].to; if (to == fath) continue; dfs(to, u); } }
intlca(int a, int b){ if (dep[a] < dep[b]) swap(a, b); for (int k = K-1; k >= 0; k--) if (dep[fa[a][k]] >= dep[b]) a = fa[a][k]; if (a == b) return a;
for (int k = K-1; k >= 0; k--) if (fa[a][k] != fa[b][k]) a = fa[a][k], b = fa[b][k];
return fa[a][0]; }
signedmain(){ cin >> n >> q; for (int i = 1, a, b; i < n; i++) { cin >> a >> b; add(a, b), add(b, a); } dfs(1, 0);
for (int i = 1; i <= n; i++) p[i] = i, sz[i] = 1;
for (int i = 1, a, b; i <= q; i++) { cin >> a >> b; int u = lca(a, b); while (dep[a] > dep[u]) merge(a, fa[a][0]), a = find(a); while (dep[b] > dep[u]) merge(b, fa[b][0]), b = find(b); }
for (int i = 1; i <= n; i++) if (p[i] == i) cnt[sz[i]]++;
bitset<N> f; f[0] = 1; for (int val = 1; val <= n; val++) { int x = 1; while (cnt[val] - x >= 0) { f |= f << (val * x); cnt[val] -= x; x <<= 1; }
if (cnt[val]) f |= f << (cnt[val] * val); } for (int i = 0; i <= n; i++) if (f[i]) ans[abs(i-(n-i))] = true; for (int i = 0; i <= n; i++) if (ans[i]) cout << i << ' ';
cout << '\n'; return0; }
加边
给定 n 个点 m 条的带权无向图,给定序列 a,表示花费 ai+aj 可以将边 (i,j) 的权修改为正无穷,q 个询问,每个询问代表将所有 ≥x 的边连起来后,最小花多少代价可以将整个图连通。
不难发现如果保证 x 从大到小排列,那么每过一个询问都可以加入新的边,并且维护这些连通块,所以想到离线询问加上并查集维护。
signedmain(){ freopen("add.in", "r", stdin); freopen("add.out", "w", stdout); cin >> n >> m >> q; for (int i = 1; i <= n; i++) cin >> a[i]; int MIN = 0x3f3f3f3f; for (int i = 1; i <= n; i++) p[i] = i, mn[i] = a[i], sum += a[i], MIN = min(MIN, a[i]); cnt = n;
for (int i = 1; i <= m; i++) { cin >> edge[i].a >> edge[i].b >> edge[i].c; } for (int i = 1, x; i <= q; i++) { cin >> x; qry[i].id = i, qry[i].x = x; } sort(edge+1, edge+1+m); int tp = 1; sort(qry+1, qry+1+q); for (int i = 1; i <= q; i++) { while (tp <= m && edge[tp].c >= qry[i].x) { int a = edge[tp].a, b = edge[tp].b; int pa = find(a), pb = find(b); if (pa != pb) { sum -= max(mn[pb], mn[pa]); p[pa] = pb; mn[pb] = min(mn[pb], mn[pa]); cnt--; } tp++; } ans[qry[i].id] = MIN * (cnt-2) + sum; } for (int i = 1; i <= q; i++) cout << ans[i] << '\n'; return0; }