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;
}
posted @ 2025-10-29 17:13  tmp_get_zip_diff  阅读(11)  评论(0)    收藏  举报