[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$所在子树)

 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 } 
View Code

 

posted @ 2022-05-22 19:32  PYWBKTDA  阅读(145)  评论(0编辑  收藏  举报