题解 P7353 [2020-2021 集训队作业] Tom & Jerry
ix35 论文《综述图论中连通性及相关问题的一些处理方法》中的一道题。
注意到点双有一个良好的性质:在点双中,从任意一点能够不经过指定点到达另一点。这个性质保证了 Jerry 能够在点双中任意行走,也提示我们用点双的角度思考问题。
考虑建出圆方树,假设 Tom 所在的结点
用形式化的语言来描述它:设
不过,如果能够 找到一个点
这两个就是全部的获胜条件了吗?确实是的,下面我们考虑证明必要性。如果两个条件均不满足,那么 Tom 必然会走到一个分支结点
考虑用 DP 来判断这两个条件的可行性。设
对于
- 圆点:
f(x)=1 当且仅当它的所有方儿子y 都有f(y)=1 。 - 方点:
f(x)=1 当且仅当它的所有圆儿子y 都有f(y)=1 ,且(fa_x,y) 是好的。
对于
-
-
- 对于
fa_x 除x 以外的其它子树y 均满足f(y)=1 。
事实上,第二个条件只需要满足这个点在该点双中的度数等于节点数减一即可,因为原图是无重边和自环的。
那么两个获胜规则的判定就很简单了,第二个就是能够找到一个点
时间复杂度
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rof(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int Maxn=2e5;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
struct Node{int frm,to,nxt;} Edge[Maxn*2+5];
int tot=1,Head[Maxn+5];
inline void Addedge(int x,int y) {Edge[++tot]=(Node){x,y,Head[x]},Head[x]=tot;}
int n,m,q,dfn[Maxn+5],low[Maxn+5],st[Maxn+5],top,cur,tmp;
int dep[Maxn+5],f[Maxn+5],g[Maxn+5],anc[Maxn+5][20],cnt[Maxn+5];
vector<int> v[Maxn+5];
vector<pair<int,int>> w[Maxn+5];
map<int,int> mp[Maxn+5];
inline int Get(int x,int y) {return dep[x]>dep[y]?anc[x][0]:anc[y][0];}
inline int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
Rof(i,19,0) if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
if(x==y) return x;
Rof(i,19,0) if(anc[x][i]==anc[y][i]) x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
inline int Jump(int x,int y) {Rof(i,19,0) if(y&(1<<i)) x=anc[x][i]; return x;}
inline void dfs(int x,int fa)
{
dep[x]=dep[fa]+1,f[x]=1,anc[x][0]=fa;
For(i,1,19) anc[x][i]=anc[anc[x][i-1]][i-1];
if(x<=n) {for(auto y:v[x]) if(y!=fa) dfs(y,x),f[x]&=f[y];}
else
{
for(auto y:v[x]) if(y!=fa)
{
dfs(y,x),f[x]&=f[y];
if(mp[fa].find(y)==mp[fa].end()) f[x]=0;
}
}
}
inline void dfs2(int x,int fa)
{
if(!g[x]) {for(auto y:v[x]) if(y!=fa) g[y]=0,dfs2(y,x); return;}
if(x<=n)
{
int pre=1; for(auto y:v[x]) if(y!=fa) g[y]&=pre,pre&=f[y];
reverse(v[x].begin(),v[x].end()),pre=1;
for(auto y:v[x]) if(y!=fa) g[y]&=pre,pre&=f[y];
reverse(v[x].begin(),v[x].end());
for(auto y:v[x]) if(y!=fa) dfs2(y,x);
return;
}
int ch=0,now=0;
for(auto y:v[x]) if(y!=fa) ch++,now+=f[y],cnt[y]=0;
for(auto i:w[x]) cnt[i.first]++,cnt[i.second]++;
for(auto y:v[x]) if(cnt[y]==ch && now-f[y]==ch-1) g[y]=1;
for(auto y:v[x]) if(y!=fa) dfs2(y,x);
}
inline void Tarjan(int x)
{
dfn[x]=low[x]=++cur,st[++top]=x;
for(int i=Head[x];i;i=Edge[i].nxt)
{
int y=Edge[i].to;
if(!dfn[y])
{
Tarjan(y),low[x]=min(low[x],low[y]);
if(low[y]==dfn[x])
{
v[x].push_back(++tmp),v[tmp].push_back(x);
while(1)
{
int k=st[top--]; v[k].push_back(tmp),v[tmp].push_back(k);
if(y==k) break;
}
}
}
else low[x]=min(low[x],dfn[y]);
}
}
int main()
{
n=read(),m=read(),q=read(),tmp=n;
For(i,1,m)
{
int a=read(),b=read();
Addedge(a,b),Addedge(b,a);
mp[a][b]=mp[b][a]=1;
}
Tarjan(1),dfs(1,0);
For(i,1,m)
{
int a=Edge[i*2].frm,b=Edge[i*2].to;
w[Get(a,b)].emplace_back(a,b);
}
g[1]=1,dfs2(1,0);
For(i,1,n) if(f[i] && g[i]) {while(q--) printf("Yes\n"); return 0;}
while(q--)
{
int a=read(),b=read(),l=LCA(a,b);
if(l!=a) printf("%s\n",g[a]?"Yes":"No");
else l=Jump(b,dep[b]-dep[a]-1),printf("%s\n",f[l]?"Yes":"No");
}
return 0;
}