[luogu7353]Tom & Jerry
建立(广义)圆方树,具体如下——
称原图中的点为圆点,对每一个点双建立方点,并向其包含的(圆)点连边
记$V(a,b)$为(原图中)删除$a$后$b$所在连通块(的点集)
称$u\rightarrow v$当且仅当圆方树上两点路径中相邻圆点在原图中有边相连
结论:Tom能在有限次行动内获胜当且仅当满足以下条件之一
- $\forall v\in V(a,b),a\rightarrow v$
- $\exists u\in [1,n]$满足$\forall v\in [1,n],u\rightarrow v$
引理:删除$a$后原图和圆方树(圆点)连通性相同
根据引理,充分性显然(直接在圆方树上"逼"即可),下面考虑必要性:
Jerry第一次移动到$a$子树内最深的不与祖父在原图中有边相连的圆点
Tom要移动到$a$最后一步必然经过$a$的兄弟,且该兄弟能$\rightarrow $其子树内所有点(否则与$a$最深矛盾)
根据第2个条件,其子树外存在其$\not\rightarrow $的点,进而重复此过程即可
显然简单树形dp即可,时间复杂度为$o(n\log n)$,可以通过
(带$\log$时因为dp时检验边是否存在&询问时找$y$所在子树)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 200005 4 int n,m,q,V,x,y,st[N],dfn[N],low[N],sz[N],g[N],f[N]; 5 vector<int>e[N],E[N],son[N];vector<int>::iterator it; 6 bool cmp(int x,int y){ 7 return dfn[x]<dfn[y]; 8 } 9 bool find(int x,int y){ 10 it=lower_bound(e[x].begin(),e[x].end(),y); 11 return (it!=e[x].end())&&((*it)==y); 12 } 13 void dfs1(int k,int fa){ 14 if (fa)st[++st[0]]=k; 15 dfn[k]=low[k]=++dfn[0]; 16 for(int i:e[k]) 17 if (i!=fa){ 18 if (dfn[i])low[k]=min(low[k],dfn[i]); 19 else{ 20 dfs1(i,k),low[k]=min(low[k],low[i]); 21 if (low[i]>=dfn[k]){ 22 E[++V].push_back(k),E[k].push_back(V); 23 while (E[V].back()!=i){ 24 E[V].push_back(st[st[0]]); 25 E[st[st[0]--]].push_back(V); 26 } 27 } 28 } 29 } 30 } 31 void dfs2(int k,int fa){ 32 dfn[k]=++dfn[0],sz[k]=f[k]=1; 33 if (k>n){ 34 for(int i:E[k]) 35 if ((i!=fa)&&(!find(fa,i))){f[k]=0;break;} 36 } 37 for(int i:E[k]) 38 if (i!=fa){ 39 dfs2(i,k),sz[k]+=sz[i],f[k]&=f[i]; 40 if (k<=n)son[k].push_back(i); 41 } 42 } 43 void dfs3(int k,int fa){ 44 int cnt=0,pos=0; 45 for(int i:E[k]) 46 if ((i!=fa)&&(!f[i]))cnt++,pos^=i; 47 for(int i:E[k]) 48 if (i!=fa){ 49 g[i]=g[k]; 50 if ((cnt>1)||(cnt==1)&&(i!=pos))g[i]=0; 51 } 52 if (k>n){ 53 for(int i:E[k]) 54 if (i!=fa){ 55 if (!find(i,fa)){g[i]=0;continue;} 56 for(int j:E[k]) 57 if ((j!=fa)&&(j!=i)&&(!find(i,j))){g[i]=0;break;} 58 } 59 } 60 for(int i:E[k]) 61 if (i!=fa)dfs3(i,k); 62 } 63 int main(){ 64 scanf("%d%d%d",&n,&m,&q); 65 for(int i=1;i<=m;i++){ 66 scanf("%d%d",&x,&y); 67 e[x].push_back(y),e[y].push_back(x); 68 } 69 for(int i=1;i<=n;i++)sort(e[i].begin(),e[i].end()); 70 V=n,dfs1(1,0); 71 dfn[0]=0,dfs2(1,0); 72 g[1]=1,dfs3(1,0); 73 for(int i=1;i<=n;i++) 74 if (f[i]&g[i]){ 75 while (q--)printf("Yes\n"); 76 return 0; 77 } 78 while (q--){ 79 scanf("%d%d",&x,&y); 80 if ((dfn[y]<dfn[x])||(dfn[x]+sz[x]<=dfn[y])){ 81 if (g[x])printf("Yes\n"); 82 else printf("No\n"); 83 } 84 else{ 85 it=--upper_bound(son[x].begin(),son[x].end(),y,cmp); 86 if (f[*it])printf("Yes\n"); 87 else printf("No\n"); 88 } 89 } 90 return 0; 91 }