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<= 2*10^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
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
1
3
0
1
2
3
Solution
突然发现我早就学过圆方树的建树方法啊……
我先大体说一下圆方树到底是个啥东东_(:зゝ∠)_
所谓的圆方树,就是把一个图变成一个树。具体怎么变呢?对于每一个点双,我们创立一个新的点,然后把这个点双内的点连接到新点上。新点就是方点,旧点就是圆点。可以证明这样连接出来的一定是一棵树。盗一张图QAQ
这个图能够很直观的体现出圆方树的建立方法……
圆方树有那么几个性质。
1、一条边两端一定是一个圆点和一个方点。
2、原图中所有的割点,在圆方树中一定至少有两条出边,非割点一定有且只有一条出边。
3、一个圆点到圆点的简单路径代表了原图中两点之间简单路径的并。这个想想也很好懂。那么我们圆点维护点原本的信息,方点维护点双内的信息就可以处理路径问题了。举个例子:
一张图,每个点有个点权,求$u$点到$v$点的所有简单路径经过的最小权值。
按照上面所说,我们每个用方点维护点双内的最小点权,然后就相当于树上查询路径最小值了。
那么要修改的话呢?改一个圆点后,总不能暴力扫一遍周围的方点吧?
其实可以方点维护的信息不包括父亲圆点,这样修改一个圆点的时候只改一下父亲方点就好了。查询的时候如果$LCA$是方点,再把$lca$的父亲单独算进去就好了。
回到这个题,首先一看询问的$sigma$是$O(n)$级别的,那么十有八九就是得上虚树了。
我们先把圆方树建出来,然后每次询问建立虚树。
很容易发现,虚树上非关键点的圆点的个数就是答案。当然,也要包括虚树上每一条边在圆方树上经过的非关键圆点。
统计的时候注意根据写法搞好边界状态= =……写这个题才发现我之前的点双写的都是假的
Code
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<vector> 5 #include<algorithm> 6 #define N (200009) 7 using namespace std; 8 9 struct Edge{int from,to,next;}edge[N<<1]; 10 int T,n,m,u,v,num_edge,dfs_num,BCC_num,top,k,q,ans; 11 int head[N],ID[N],Low[N],DFN[N],stack[N],Cut[N]; 12 int vis[N],f[N][18],Depth[N],a[N],Sum[N],Key[N]; 13 vector<int>BCC[N]; 14 vector<int>E[N]; 15 bool cmp(int u,int v) {return DFN[u]<DFN[v];} 16 17 void add(int u,int v) 18 { 19 edge[++num_edge].to=v; 20 edge[num_edge].from=u; 21 edge[num_edge].next=head[u]; 22 head[u]=num_edge; 23 } 24 25 void Tarjan(int x,int fa) 26 { 27 int son_num=0; 28 Low[x]=DFN[x]=++dfs_num; 29 for (int i=head[x]; i; i=edge[i].next) 30 if (!DFN[edge[i].to]) 31 { 32 son_num++; stack[++top]=i; 33 Tarjan(edge[i].to,x); 34 Low[x]=min(Low[x],Low[edge[i].to]); 35 if (Low[edge[i].to]>=DFN[x]) 36 { 37 Cut[x]=true; ++BCC_num; 38 while (1) 39 { 40 int now=stack[top--]; 41 if (ID[edge[now].from]!=BCC_num) 42 { 43 BCC[BCC_num].push_back(edge[now].from); 44 ID[edge[now].from]=BCC_num; 45 } 46 if (ID[edge[now].to]!=BCC_num) 47 { 48 BCC[BCC_num].push_back(edge[now].to); 49 ID[edge[now].to]=BCC_num; 50 } 51 if (edge[now].from==x && edge[now].to==edge[i].to) break; 52 } 53 } 54 } 55 else if (edge[i].to!=fa) 56 Low[x]=min(Low[x],DFN[edge[i].to]); 57 if (!fa) Cut[x]=(son_num>1); 58 } 59 60 void Build_Tree1()//建圆方树 61 { 62 Tarjan(1,0); 63 memset(head,0,sizeof(head)); num_edge=0; 64 for (int i=1; i<=BCC_num; ++i) 65 for (int j=0,sz=BCC[i].size(); j<sz; ++j) 66 add(i+n,BCC[i][j]), add(BCC[i][j],i+n), vis[BCC[i][j]]=1; 67 } 68 69 void DFS(int x,int fa) 70 { 71 f[x][0]=fa; Depth[x]=Depth[fa]+1; 72 DFN[x]=++dfs_num; Sum[x]=Sum[fa]+vis[x]; 73 for (int i=1; i<=17; ++i) 74 f[x][i]=f[f[x][i-1]][i-1]; 75 for (int i=head[x]; i; i=edge[i].next) 76 if (edge[i].to!=fa) DFS(edge[i].to,x); 77 } 78 79 int LCA(int x,int y) 80 { 81 if (Depth[x]<Depth[y]) swap(x,y); 82 for (int i=17; i>=0; --i) 83 if (Depth[f[x][i]]>=Depth[y]) x=f[x][i]; 84 if (x==y) return x; 85 for (int i=17; i>=0; --i) 86 if (f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i]; 87 return f[x][0]; 88 } 89 90 void ADD(int u,int v) 91 { 92 if (u==v) return; 93 E[u].push_back(v); 94 ans+=Sum[v]-Sum[u]; 95 } 96 97 void Insert(int x) 98 { 99 if (top==1) {stack[++top]=x; return;} 100 int lca=LCA(x,stack[top]); 101 if (lca==stack[top]) {stack[++top]=x; return;} 102 while (top>1 && DFN[stack[top-1]]>=DFN[lca]) 103 ADD(stack[top-1],stack[top]), top--; 104 if (lca!=stack[top]) ADD(lca,stack[top]), stack[top]=lca; 105 stack[++top]=x; 106 } 107 108 void Build_Tree2()//建虚树 109 { 110 stack[top=1]=1; 111 for (int i=1; i<=k; ++i) Insert(a[i]); 112 while (top>=2) ADD(stack[top-1],stack[top]), top--; 113 ans+=vis[1]; 114 if (!Key[1] && E[1].size()==1) 115 ans-=Sum[f[E[1][0]][0]]; 116 } 117 118 void Clear(int x) 119 { 120 Key[x]=0; 121 for (int sz=E[x].size(),i=0; i<sz; ++i) 122 Clear(E[x][i]); 123 E[x].clear(); 124 } 125 126 void MEMSET() 127 { 128 memset(ID,0,sizeof(ID)); 129 memset(DFN,0,sizeof(DFN)); 130 memset(Low,0,sizeof(Low)); 131 memset(Cut,0,sizeof(Cut)); 132 memset(vis,0,sizeof(vis)); 133 memset(head,0,sizeof(head)); num_edge=0; 134 for (int i=1; i<=n; ++i) BCC[i].clear(); 135 dfs_num=0; BCC_num=0; top=0; ans=0; 136 } 137 138 int main() 139 { 140 scanf("%d",&T); 141 while (T--) 142 { 143 MEMSET(); 144 scanf("%d%d",&n,&m); 145 for (int i=1; i<=m; ++i) 146 { 147 scanf("%d%d",&u,&v); 148 add(u,v); add(v,u); 149 } 150 Build_Tree1(); 151 dfs_num=0; DFS(1,0); 152 scanf("%d",&q); 153 for (int i=1; i<=q; ++i) 154 { 155 ans=0; 156 scanf("%d",&k); 157 for (int j=1; j<=k; ++j) 158 scanf("%d",&a[j]), Key[a[j]]=1; 159 sort(a+1,a+k+1,cmp); 160 Build_Tree2(); 161 printf("%d\n",ans-k); Clear(1); 162 } 163 } 164 }

浙公网安备 33010602011771号