1. 程式人生 > >bzoj5329: [Sdoi2018]戰略遊戲 虛樹+圓方樹

bzoj5329: [Sdoi2018]戰略遊戲 虛樹+圓方樹

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|<= 2

10^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;
}