bzoj5329: [Sdoi2018]战略游戏 虚树+圆方树

xiaoxiao2022-05-17  16

bzoj5329: [Sdoi2018]战略游戏

Description

省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到 任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这 个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不 能走到v,那么小Q就能赢下这一局游戏。 小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S 你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

Input

第一行包含一个正整数T,表示测试数据的组数, 对于每组测试数据, 第一行是两个整数n和m,表示地图的城市数和道路数, 接下来m行,每行包含两个整数u和v~(1<=u<v<=n) 表示第u个城市和第v个城市之间有一条道路,同一对城市之间可能有多条道路连接, 第m+1是一个整数q,表示游戏的局数, 接下来q行,每行先给出一个整数|S|(2<=|S|<=n) 表示小C占领的城市数量,然后给出|S|个整数s1,s2,…s|S|,(1<=s1<s2<s|S|<=n),表示小C占领的城市。 1<= T<= 10, 2<= n<= 10^5 且 n-1<= m<= 210^5, 1<= q<= 10^5, 对于每组测试数据,有Sigma|S|<= 210^5

Output

对于每一局游戏,输出一行,包含一个整数,表示这一局游戏中有多少个城市在小Q摧毁之后能够让他赢下这一局游戏。

Sample Input

2 7 6 1 2 1 3 2 4 2 5 3 6 3 7 3 2 1 2 3 2 3 4 4 4 5 6 7 6 6 1 2 1 3 2 3 1 4 2 5 3 6 4 3 1 2 3 3 1 2 6 3 1 5 6 3 4 5 6

Sample Output

0 1 3 0 1 2 3

分析

单单考虑一组询问。 如果是两个点,就是圆方树路径上圆点个数。 多个点就是圆方树上所有点路径的并的圆点个数。 显然还是一颗树,所以采用虚树。 不用建出虚树,只需要在建虚树的过程中顺便算一下距离即可。 多组数据差评。

代码

#include<bits/stdc++.h> const int N = 2e5 + 10, M = 4e5 + 10; int ri() { char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1; for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f; } int di[N], in[N], n, tot; struct Edge { int to[M], nx[M], pr[N], tp; void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;} void adds(int u, int v) {add(u, v); add(v, u);} void Clr() {memset(pr, 0, sizeof(pr)); tp = 0;} }; struct Round_Sqare_Tree { Edge T; int fa[N], de[N], sz[N], ds[N], d[N], tot; void Clr() {T.Clr(); tot = 0;} void dfs1(int u, int ff) { fa[u] = ff; de[u] = de[ff] + 1; di[u] = di[ff] + (u <= n); sz[u] = 1; ds[u] = 0; in[u] = ++tot; for(int i = T.pr[u], v; i; i = T.nx[i]) if((v = T.to[i]) != ff) dfs1(v, u), sz[u] += sz[v], sz[ds[u]] < sz[v] ? ds[u] = v : 0; } void dfs2(int u, int c) { d[u] = c; if(!ds[u]) return ; dfs2(ds[u], c); for(int i = T.pr[u]; i; i = T.nx[i]) if(T.to[i] != fa[u] && T.to[i] != ds[u]) dfs2(T.to[i], T.to[i]); } int Lca(int u, int v) { for(;d[u] != d[v]; u = fa[d[u]]) de[d[u]] < de[d[v]] ? u ^= v ^= u ^= v : 0; return de[u] < de[v] ? u : v; } }rst; struct Tarjan { Edge G; int st[N], dfn[N], low[N], tp, tm; void Clr() { G.Clr(); tm = tp = 0; memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(st, 0, sizeof(st)); } void dfs(int u, int ff) { st[++tp] = u; dfn[u] = low[u] = ++tm; for(int i = G.pr[u], v; i; i = G.nx[i]) if((v = G.to[i]) != ff) { if(!dfn[v]) { dfs(v, u); low[u] = std::min(low[u], low[v]); if(low[v] >= dfn[u]) for(rst.T.adds(u, ++tot); st[tp + 1] != v;) rst.T.adds(st[tp--], tot); } else low[u] = std::min(low[u], dfn[v]); } } }tar; bool cmp(int a, int b) {return in[a] < in[b];} struct Itree { int st[N], h[N], k, r, rt, tp; void Up(int u, int v) {r += di[v] - di[u];} void Ins(int u) { if(tp <= 1) return void(st[++tp] = u); int c; if((c = rst.Lca(u, st[tp])) == st[tp]) return void(st[++tp] = u); for(;tp > 1 && in[st[tp - 1]] >= in[c]; --tp) Up(st[tp - 1], st[tp]); if(c != st[tp]) Up(c, st[tp]), st[tp] = c; st[++tp] = u; } void Build() { std::sort(h + 1, h + k + 1, cmp); rt = st[tp = 1] = rst.Lca(h[1], h[k]); for(int i = 1;i <= k; ++i) Ins(h[i]); for(;--tp;) Up(st[tp], st[tp + 1]); } void Work() { k = ri(); for(int i = 1;i <= k; ++i) h[i] = ri(); Build(); printf("%d\n", r + (rt <= n) - k); r = 0; } }it; int main() { for(int T = ri();T--;) { tar.Clr(); rst.Clr(); tot = n = ri(); for(int m = ri();m--;) tar.G.adds(ri(), ri()); tar.dfs(1, 0); rst.dfs1(1, 0); rst.dfs2(1, 1); for(int q = ri();q--;) it.Work(); } return 0; }
转载请注明原文地址: https://www.6miu.com/read-4884237.html

最新回复(0)