[LibreOJ 2562][SDOI2018]战略游戏
aji圆方树毁我青春,,,省选现场看出来是圆方树但写不出点双滚粗。
显然可以割的点是某两个点的某一条简单路径上的割点,然后两点间的简单路劲的并就是圆方树上的路径,割点在圆方树上就是非叶子的圆点。我们求所有两点路径的并,就需要虚树。
然后做完了……直接建圆方树,每次询问建虚树xjb统计即可。
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <utility> #include <vector> #include <stack> #include <queue> #include <map> #include <set> const int maxn = 100005; const int maxm = 200005; std::vector<int> G[maxn]; void cz1() { for(int i = 0; i < maxn; i ++) { G[i].clear(); } } void add_edge1(int u, int v) { G[u].push_back(v); G[v].push_back(u); } using pii = std::pair<int, int>; int dcnt, bcc_cnt; int pre[maxn]; bool iscut[maxn]; int bccno[maxn]; std::vector<int> bcc[maxm]; int dfs(int x, int fa = -1) { static std::stack<pii> S; pre[x] = ++ dcnt; int low = pre[x]; int cld = 0; for(auto v : G[x]) { pii e = std::make_pair(x, v); if(!pre[v]) { S.push(e); cld ++; int lowv = dfs(v, x); low = std::min(low, lowv); if(lowv >= pre[x]) { iscut[x] = true; bcc_cnt ++; bcc[bcc_cnt].clear(); while(true) { pii ee = S.top(); S.pop(); int f = ee.first, g = ee.second; if(bccno[f] != bcc_cnt) { bccno[f] = bcc_cnt; bcc[bcc_cnt].push_back(f); } if(bccno[g] != bcc_cnt) { bccno[g] = bcc_cnt; bcc[bcc_cnt].push_back(g); } if(f == x && g == v) break; } } } else if(pre[v] < pre[x] && v != fa) { S.push(e); low = std::min(low, pre[v]); } } return low; } int n, m; void calc_bcc() { std::fill(pre, pre + 1 + n, 0); std::fill(iscut, iscut + 1 + n, false); std::fill(bccno, bccno + 1 + m, 0); dcnt = bcc_cnt = 0; for(int i = 1; i <= n; i ++) { if(!pre[i]) dfs(i); } } std::vector<int> G2[maxn + maxm]; void cz2() { for(int i = 0; i < maxn + maxm; i ++) G2[i].clear(); } void add_edge2(int u, int v) { G2[u].push_back(v); G2[v].push_back(u); } int anc[maxn + maxm][19], dep[maxn + maxm], d[maxn + maxm]; int dfn[maxn + maxm], siz[maxn + maxm]; int cnt2; void dfs_tree(int x, int fa = -1, int depth = 0, int s = 0) { #ifdef LOCAL printf("V (%d, %d, %d, %d)\n", x, fa, depth, s); #endif cnt2 ++; dfn[x] = cnt2; siz[x] = 1; d[x] = s; anc[x][0] = fa; dep[x] = depth; for(auto v : G2[x]) { if(v != fa) { dfs_tree(v, x, depth + 1, s + ((v <= n) ? 1 : 0)); siz[x] += siz[v]; } } } void process_lca() { memset(anc, -1, sizeof(anc)); cnt2 = 0; int s = bcc_cnt + n; dfs_tree(n + 1); #ifdef LOCAL printf("s : %d\n", s); #endif for(int j = 1; (1 << j) < s; j ++) { for(int i = 1; i <= s; i ++) { int a = anc[i][j - 1]; if(a != -1) anc[i][j] = anc[a][j - 1]; } } } int lca(int x, int y) { if(dep[x] < dep[y]) std::swap(x, y); for(int j = 18; j >= 0; j --) { int a = anc[x][j]; if(a != -1 && dep[a] >= dep[y]) { x = a; } } if(x == y) return x; for(int j = 18; j >= 0; j --) { int a1 = anc[x][j], a2 = anc[y][j]; if(a1 != -1 && a2 != -1 && a1 != a2) { x = a1; y = a2; } } return anc[x][0]; } void process() { calc_bcc(); cz2(); for(int i = 1; i <= bcc_cnt; i ++) { int id = n + i; for(auto u : bcc[i]) { add_edge2(u, id); } } process_lca(); } int get_delta(int x, int y) { // y is x's ancestor. return (d[anc[x][0]] - d[y]); } bool is_anc(int fa, int bef) { int l1 = dfn[fa], r1 = dfn[fa] + siz[fa] - 1; int l2 = dfn[bef], r2 = dfn[bef] + siz[bef] - 1; return (l1 <= l2 && r2 <= r1); } int query(int sz) { std::vector<int> V; for(int i = 1; i <= sz; i ++) { int x; scanf("%d", &x); V.push_back(x); } std::sort(V.begin(), V.end(), [&](const int &i, const int &j) { return dfn[i] < dfn[j]; }); std::set<int> ds; ds.insert(V[0]); for(int i = 1; i < sz; i ++) { ds.insert(V[i]); ds.insert(lca(V[i - 1], V[i])); } V.clear(); for(auto u : ds) V.push_back(u); std::sort(V.begin(), V.end(), [&](const int &i, const int &j) { return dfn[i] < dfn[j]; }); std::stack<int> S; int ans = 0; for(int i = 0; i < V.size(); i ++) { int u = V[i]; while(!S.empty() && !is_anc(S.top(), u)) { S.pop(); } if(!S.empty()) { #ifdef LOCAL printf("ans (%d, %d) : %d\n", u, S.top(), get_delta(u, S.top())); #endif ans += get_delta(u, S.top()); } S.push(u); } for(auto u : ds) { if(u <= n) ans ++; } ans -= sz; return ans; } int main() { int T; scanf("%d", &T); while(T --) { scanf("%d%d", &n, &m); cz1(); for(int i = 1; i <= m; i ++) { int u, v; scanf("%d%d", &u, &v); add_edge1(u, v); } process(); int q; scanf("%d", &q); while(q --) { int sz; scanf("%d", &sz); printf("%d\n", query(sz)); #ifdef LOCAL fflush(stdout); #endif } } return 0; }
[LA 5135][WF2011]Mining Your Own Business
点双开坑……
考虑每个点双。如果一个点双有一个(全局的)割点,那么必须要在该点双的一个非割点处建一个出口(否则割掉这个割点会出事)。如果一个点双的割点多于一个,那么其实并没有必要在这里建出口,因为不管割掉那个点,这个点双里面的点总有办法走到一个只有一个割点的点双里。
有一种特殊情况……就是整个图是点双联通的,这样的话随便找两个点建出口就行了。
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <utility> #include <vector> #include <stack> #include <map> const int maxn = 100005; std::vector<int> G[maxn]; int n; void clear_graph() { for(int i = 1; i <= 100000; i ++) { G[i].clear(); } } void add_edge(int u, int v) { G[u].push_back(v); G[v].push_back(u); } std::map<int, int> ma; int sz; int trace(int x) { if(ma.count(x)) { return ma[x]; } else { ma[x] = ++ sz; return sz; } } using Edge = std::pair<int, int>; int dcnt, bcc_cnt; int pre[maxn]; bool is_cut[maxn]; int bccno[maxn]; std::vector<int> bcc[maxn]; std::stack<Edge> S; int dfs(int x, int fa = -1) { pre[x] = ++ dcnt; int low = pre[x]; int cld = 0; for(auto v : G[x]) { Edge e = std::make_pair(x, v); if(!pre[v]) { S.push(e); cld ++; int lowv = dfs(v, x); low = std::min(low, lowv); if(lowv >= pre[x]) { is_cut[x] = true; bcc_cnt ++; bcc[bcc_cnt].clear(); while(true) { Edge ee = S.top(); S.pop(); int f = ee.first, g = ee.second; if(bccno[f] != bcc_cnt) { bccno[f] = bcc_cnt; bcc[bcc_cnt].push_back(f); } if(bccno[g] != bcc_cnt) { bccno[g] = bcc_cnt; bcc[bcc_cnt].push_back(g); } if(f == x && g == v) break; } } } else if(pre[v] < pre[x] && v != fa) { S.push(e); low = std::min(low, pre[v]); } } if(fa < 0 && cld == 1) is_cut[x] = false; return low; } void calc_bcc() { std::fill(pre, pre + 1 + sz, 0); std::fill(is_cut, is_cut + 1 + sz, false); std::fill(bccno, bccno + 1 + sz, 0); dcnt = bcc_cnt = 0; for(int i = 1; i <= sz; i ++) { if(!pre[i]) dfs(i); } } using ll = long long; int main() { int cs = 0; while(scanf("%d", &n) == 1) { if(n == 0) break; cs ++; sz = 0; ma.clear(); clear_graph(); for(int i = 1; i <= n; i ++) { int u, v; scanf("%d%d", &u, &v); u = trace(u); v = trace(v); add_edge(u, v); } calc_bcc(); ll a1 = 0, a2 = 1LL; for(int i = 1; i <= bcc_cnt; i ++) { int cnum = 0; for(auto x : bcc[i]) { if(is_cut[x]) cnum ++; } if(cnum == 1) { a1 ++; a2 *= (ll(bcc[i].size() - 1)); } } if(bcc_cnt == 1) { a1 = 2; ll s = bcc[1].size(); a2 = s * (s - 1LL) / 2LL; } printf("Case %d: %lld %lld\n", cs, a1, a2); } return 0; }