P7353 [2020-2021 集训队作业] Tom & Jerry 题解
Sol
注意到 T 想赢必须一步一步缩小 J 的移动空间,所以 T 最优只会移动到割点来缩小 J 的移动空间最终让 J 无处可移。
所以我们考虑建出原图的圆方树。
考虑对于一组询问,把 \(a\) 提起来作为根,那么设 \(b\) 是 \(a\) 的子节点 \(c\) 子树中的节点,那么第一步肯定移动到 \(c\) 的子树中的节点,所以我们只关心在 \(c\) 的子树中是否所有点为 J 的位置时,T 有必胜策略(即可以通过上述方法追到 J)。
你会发现这个显然是可以 dp 的,设 \(dp_{i}\) 表示 J 在以 \(a\) 为全局根,以 \(i\) 为根的子树时,T 是否可以抓到 J,答案是 \(dp_{c}\)。
肯定不能每个询问都提个根跑 dp,可以用一种类似与换根的东西,由于以 \(a\) 作为根的子结点,实际上就是以 \(1\) 作为根 \(a\) 的子结点加上 \(a\) 的父亲(如果有父亲的话),所以再设 \(f_{i}\) 表示 J 在 \(i\) 的子树外的时,T 是否可以抓到 J,这样就只用跑一遍了,具体转移看代码。
还有一个特殊情况,如果存在一个点 \(u\),以 \(u\) 作为根时是否 J 无论在哪个点,T 都可以抓到 J,那么 T 移到 \(u\) 后一定有策略抓住 J,此时 T 一定能获胜,判一下 \(dp_{u}\) 和 \(f_{u}\) 是否同时可行即可。
时间复杂度 \(O(n \log n)\)。(此处认为 \(n,m,q\) 同阶)
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x(0);
char ch(getchar());
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x;
}
const int N=2e5+5;
int n,m,q,ext,dfn[N],low[N],bh[N],siz[N],hvy[N],zg[N],tot,cnt,fa[N],dep[N];
vector<int>nbrg[N],nbrt[N];
set<int>st[N];
bool dp1[N],dp2[N];
stack<int>stk;
void Tarjan(int cur)
{
dfn[cur]=low[cur]=++tot;
stk.push(cur);
for(int nxt:nbrg[cur])
{
if(dfn[nxt]==0)
{
Tarjan(nxt);
low[cur]=min(low[cur],low[nxt]);
if(low[nxt]==dfn[cur])
{
ext++;
while(stk.top()!=nxt)
{
int x=stk.top();
stk.pop();
nbrt[x].push_back(ext);
nbrt[ext].push_back(x);
}
stk.pop();
nbrt[nxt].push_back(ext);
nbrt[ext].push_back(nxt);
nbrt[cur].push_back(ext);
nbrt[ext].push_back(cur);
}
}
else
low[cur]=min(low[cur],dfn[nxt]);
}
return ;
}
void dfs(int cur,int fa)
{
::fa[cur]=fa;
dep[cur]=dep[fa]+1;
siz[cur]=1;
bh[cur]=++cnt;
dp1[cur]=true;
for(int nxt:nbrt[cur])
{
if(nxt==fa)
continue;
dfs(nxt,cur);
siz[cur]+=siz[nxt];
if(siz[nxt]>siz[hvy[cur]])
hvy[cur]=nxt;
dp1[cur]&=dp1[nxt];
if(cur>n&&!st[fa].count(nxt))
dp1[cur]=false;
}
return ;
}
void second_dfs(int cur)
{
if(fa[cur]==cur)
zg[cur]=zg[fa[cur]];
else
zg[cur]=cur;
set<int>stt;
stt.clear();
stt.insert(fa[cur]);
int num=0;
for(int nxt:nbrt[cur])
{
if(nxt==fa[cur])
continue;
stt.insert(nxt);
if(dp1[nxt]==false)
num++;
}
for(int nxt:nbrt[cur])
{
if(nxt==fa[cur])
continue;
dp2[nxt]=dp2[cur]&&(num==0||(num==1&&dp1[nxt]==false));
if(cur>n)
{
int num=1;
for(int u:nbrg[nxt])
if(stt.count(u))
num++;
if(num!=(int)stt.size())
dp2[nxt]=false;
}
}
for(int nxt:nbrt[cur])
if(nxt!=fa[cur])
second_dfs(nxt);
return ;
}
inline int jump(int x,int y)
{
while(dep[x]<dep[y])
{
y=zg[y];
if(fa[y]==x)
return y;
y=fa[y];
}
return hvy[x];
}
signed main()
{
ext=n=read(),m=read(),q=read();
for(int i=1;i<=m;i++)
{
int x(read()),y(read());
st[x].insert(y);
st[y].insert(x);
nbrg[x].push_back(y);
nbrg[y].push_back(x);
}
Tarjan(1);
dfs(1,0);
dp2[1]=true;
second_dfs(1);
for(int i=1;i<=n;i++)
if(dp1[i]==true&&dp2[i]==true)
{
while(q--)
cout<<"Yes\n";
return 0;
}
while(q--)
{
int a(read()),b(read());
if(bh[a]<=bh[b]&&bh[b]<=bh[a]+siz[a]-1)
{
if(dp1[jump(a,b)]==true)
cout<<"Yes\n";
else
cout<<"No\n";
}
else if(dp2[a]==true)
cout<<"Yes\n";
else
cout<<"No\n";
}
return 0;
}

浙公网安备 33010602011771号