题目归档 #9
目录
- P6378 [PA2010] Riddle
- [JOISC 2014 Day3] 电压
- P4197 Peaks
P6378 [PA2010] Riddle
显然是个 2-SAT 问题,对于相邻的点很容易设置约束条件。然而对于一个部分最多一个关键点这个条件,如果暴力建 2-SAT 的约束边是 \(O(n^2)\) 的,显然不能过。
所以肯定要优化建图了。一开始想的是直接莽线段树优化建图,结果倒数第三个测试点一直 TLE。
看了题解,发现是 前缀和优化建图。考虑增加一倍的点 \(pre\),若其中 \(pre_i=1\) 代表在 \(j \in [1,i]\) 中有 \(a_j=1\)。
然后就可以 \(O(n)\) 地建图了。
-
代码:
int n, m, k, U[Maxn], V[Maxn], siz[Maxn], change[Maxn]; struct Edge { int next, to; } edge[Maxn * 10]; int head[Maxn * 4], edge_num; /* a: 0~2n pre: 2n~4n */ inline void add_edge(int from, int to) { edge[++edge_num].next = head[from]; edge[edge_num].to = to; head[from] = edge_num; } int dfn[Maxn * 4], low[Maxn * 4], stack[Maxn * 4], scc[Maxn * 4], top, cnt, scc_num; bool ins[Maxn * 4]; void tarjan(int u) { dfn[u] = low[u] = ++cnt; stack[++top] = u; ins[u] = 1; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { // cout << u << " " << v << endl; tarjan(v); low[u] = min(low[u], low[v]); } else if(ins[v]) low[u] = min(low[u], dfn[v]); } if(low[u] == dfn[u]) { ++scc_num; while(stack[top] != u) { ins[stack[top]] = 0; scc[stack[top--]] = scc_num; } ins[stack[top]] = 0; scc[stack[top--]] = scc_num; } } int main() { n = read(); m = read(); k = read(); for(int i = 1; i <= m; ++i) U[i] = read(), V[i] = read(); int cnt = 0; for(int i = 1; i <= k; ++i) { siz[i] = read(); for(int j = 1; j <= siz[i]; ++j) change[read()] = ++cnt; } for(int i = 1; i <= m; ++i) U[i] = change[U[i]], V[i] = change[V[i]]; cnt = 0; for(int i = 1; i <= k; ++i) { for(int j = 1; j <= siz[i]; ++j) { ++cnt; add_edge(cnt + n, cnt + 3 * n), add_edge(cnt + 2 * n, cnt); if(j > 1) add_edge(cnt - 1 + 3 * n, cnt + 3 * n), add_edge(cnt + 2 * n, cnt - 1 + 2 * n); if(j > 1) add_edge(cnt - 1 + 3 * n, cnt), add_edge(cnt + n, cnt - 1 + 2 * n); } } for(int i = 1; i <= m; ++i) add_edge(U[i], V[i] + n), add_edge(V[i], U[i] + n); for(int i = 1; i <= n * 4; ++i) if(!dfn[i]) tarjan(i); bool flag = 1; for(int i = 1; i <= n; ++i) if(scc[i] == scc[i + n]) { flag = 0; break; } if(flag) cout << "TAK" << endl; else cout << "NIE" << endl; return 0; }
[JOISC 2014 Day3] 电压
显然,选择的这条边必须满足以下两个条件:
- 在所有奇环上
- 不在任何一个偶环上
因此构建生成树。考虑所有的 返祖边 所构成的环的大小(可以通过深度算出),看是奇数还是偶数。
然后树上差分,打标记即可。
需要注意的是,返祖边能够被选,当且仅当图中仅存在 \(1\) 个奇环,这时答案加一。
-
代码:
int vis[Maxn], depth[Maxn], odd[Maxn], even[Maxn], tot; void dfs(int u, int inedge) { vis[u] = 1; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if(i == (inedge ^ 1)) continue; if(!vis[v]) depth[v] = depth[u] + 1, dfs(v, i); else if(depth[v] < depth[u]) { int len = depth[u] - depth[v] + 1; if(len & 1) ++odd[u], --odd[v], ++tot; else ++even[u], --even[v]; } } } int os[Maxn], es[Maxn], ans, root; void dfs2(int u) { vis[u] = 1; os[u] = odd[u]; es[u] = even[u]; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; if(!vis[v]) dfs2(v), os[u] += os[v], es[u] += es[v]; } if(u != root && os[u] == tot && !es[u]) ++ans; } int main() { n = read(); m = read(); int u, v; for(int i = 1; i <= m; ++i) { u = read(); v = read(); add_edge(u, v); add_edge(v, u); } for(int i = 1; i <= n; ++i) if(!vis[i]) depth[i] = 1, dfs(i, 0); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; ++i) if(!vis[i]) root = i, dfs2(i); if(tot == 1) ++ans; cout << ans << endl; return 0; }
P4197 Peaks
看到经过不大于某一值的边,考虑构建 kruskal 重构树。
重构树建好之后,对于每次询问,从点 \(v\) 跳倍增跳到恰好小于等于目标值的点,它的子树中的点可以互相通达。
然后用主席树维护子树第 \(k\) 大即可。(主席树很久没写的我特别难受。。。)
-
代码:
int n, m, q, T; int tot, all, fa[Maxn * 2], val[Maxn * 2], siz[Maxn * 2], id[Maxn * 2], root[Maxn * 2], raw[Maxn * 2]; struct Hi { int h, id, newh; } hi[Maxn * 2]; struct Node { int u, v, w; } node[Maxm]; struct Edge { int next, to; } edge[Maxn * 2]; int head[Maxn * 2], edge_num; void add_edge(int from, int to) { edge[++edge_num].next = head[from]; edge[edge_num].to = to; head[from] = edge_num; } int cmp1(Hi fir, Hi sec) {return fir.h < sec.h;} int cmp2(Node fir, Node sec) {return fir.w < sec.w;} int cmp3(Hi fir, Hi sec) {return fir.id < sec.id;} int find(int x) { if(fa[x] == x) return x; else return fa[x] = find(fa[x]); } queue <int> Q; int f[Maxn * 2][Max]; void bfs(int S) { Q.push(S); while(Q.size()) { int u = Q.front(); Q.pop(); for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; f[v][0] = u; for(int j = 1; j < Max; ++j) { f[v][j] = f[f[v][j - 1]][j - 1]; if(!f[v][j]) break; } Q.push(v); } } } struct Tree { int ls, rs; int sum; } tree[Maxn * 80]; int counter = 0; int insert(int pre, int l, int r, int x, int val) { int p = ++counter; tree[p] = tree[pre]; if(l == r) { tree[p].sum += val; return p; } int mid = (l + r) >> 1; if(x <= mid) tree[p].ls = insert(tree[p].ls, l, mid, x, val); else tree[p].rs = insert(tree[p].rs, mid + 1, r, x, val); tree[p].sum = tree[tree[p].ls].sum + tree[tree[p].rs].sum; return p; } int query(int p, int q, int l, int r, int rk) { //cout << "debug " << l << " " << r << " " << rk << " " << tree[p].sum << endl; if(l == r) { // cout << "debug " << tree[q].sum << " " << rk << " " << l << endl; if(tree[q].sum - tree[p].sum >= rk) return l; else return -1; } int diff = tree[tree[q].rs].sum - tree[tree[p].rs].sum; int mid = (l + r) >> 1; if(rk <= diff) return query(tree[p].rs, tree[q].rs, mid + 1, r, rk); else return query(tree[p].ls, tree[q].ls, l, mid, rk - diff); } int build(int l, int r) { int p = ++counter; if(l == r) return p; int mid = (l + r) >> 1; tree[counter].ls = build(l, mid); tree[counter].rs = build(mid + 1, r); return p; } int cnt = 0; void dfs(int u) { siz[u] = 1; id[u] = ++cnt; if(hi[u].newh) root[cnt] = insert(root[cnt - 1], 1, all, hi[u].newh, 1); else root[cnt] = root[cnt - 1]; // cout << "dfs " << u << " " << hi[u].newh << endl; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].to; dfs(v); siz[u] += siz[v]; } } void lsh() { sort(hi + 1, hi + n + 1, cmp1); hi[0].h = -1e9; for(int i = 1; i <= n; ++i) { if(hi[i].h == hi[i - 1].h) hi[i].newh = hi[i - 1].newh; else hi[i].newh = hi[i - 1].newh + 1; raw[hi[i].newh] = hi[i].h; } all = hi[n].newh; sort(hi + 1, hi + n + 1, cmp3); } int solve(int v, int x, int k) { for(int i = Max - 1; i >= 0; --i) { if(!f[v][i]) continue; if(val[f[v][i]] <= x) v = f[v][i]; } // cout << "debug " << v << endl; int ans = query(root[id[v]], root[id[v] + siz[v] - 1], 1, all, k); if(ans == -1) return -1; else return raw[ans]; } int main() { n = read(); m = read(); q = read(); tot = n; for(int i = 1; i <= n; ++i) hi[i].id = i, hi[i].h = read(); lsh(); for(int i = 1; i <= m; ++i) { node[i].u = read(); node[i].v = read(); node[i].w = read(); } sort(node + 1, node + m + 1, cmp2); for(int i = 1; i <= n * 2; ++i) fa[i] = i; for(int i = 1; i <= m; ++i) { int ancu = find(node[i].u), ancv = find(node[i].v); if(ancu != ancv) { val[++tot] = node[i].w; add_edge(tot, ancu); add_edge(tot, ancv); fa[ancu] = fa[ancv] = tot; } } bfs(tot); root[0] = build(1, all); dfs(tot); int v, x, k, ans = 0; for(int i = 1; i <= q; ++i) { v = read(); x = read(); k = read(); // if(T) v = v ^ ans, x = x ^ ans, k = k ^ ans; ans = solve(v, x, k); printf("%d\n", ans); if(ans == -1) ans = 0; } return 0; }

浙公网安备 33010602011771号