loading

P7353 [2020-2021 集训队作业] Tom & Jerry

题意

给定一张 \(n\)\(m\) 边的无向连通图,图上有两个玩家 T 和 J。J 为先手,J 每次能沿着边走任意多步,但是不能经过 T 的所在点。T 每次只能沿边走一步。如果存在一个时刻使得 J 和 T 在同一点上,T 胜利。若 T 无法在有限时间内抓到 J,J 胜利。\(q\) 次询问 T 和 J 初始位置 \(x,y\),求谁有必胜策略。

\(n,m,q\le 10^5\)

分析

看到不能经过 T 所在点,考虑点双,并建出圆方树,让 T 所在点作为根,如果根节点不是割点,那么 J 能到达图的任意点,这种情况后面会讨论。现在不妨设根节点是割点,假设 J 初始位置的满足是根节点儿子的最浅祖先为 \(u\),则 J 能任意在 \(u\) 子树内的点移动。而 T 为了抓到 J,肯定会逐步缩小 J 的移动范围,直至被抓到,所以 T 肯定会向根节点所属的某个点双内的另一个满足两点在原图上有边的点移动。对于一个点双来说,如果根节点能通过一条边到达该点双的任意其他节点,那么 J 只要走进这个点双,J 就逃不出去了,就完成的 J 移动范围的缩小。如果存在一个点双不满足,那么 J 就能逃出这个点双到达 \(u\) 子树外的任意点,加上 T 移动前能到达 \(u\) 子树内的任意点,就相当于能到达图的任意点,这种情况后面会讨论。

这样我们设 \(f_u\) 表示只考虑 \(u\) 子树内的点 T 能否抓到在 \(u\) 子树内的所有点。对于方点,我们让它的定义加上“\(fa_u\) 是否满足能通过一条边到达点双 \(u\) 内的所有点”。而每次根节点不同,所以考虑换根 DP,再设 \(g_u\) 表示以 \(u\) 为根时 \(fa_u\) 所属子树的 DP 值。查询的时候为了找到 \(u\) 可能要写一个树上倍增。

现在考虑 J 能到达图任意点的情况,实际上如果存在一个点满足 \(f_u=g_u=1\),那么 T 只要走到这个点就赢了,此时所有查询答案都是 T 必胜。

复杂度单 log。实现精细可能能做到线性。

点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<map>
#include<unordered_map>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<array>
#include<tuple>
#include<ctime>
#include<random>
#include<cassert>
#include<chrono>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
#define x1 xx1
#define y1 yy1
#define IOS ios::sync_with_stdio(false)
#define ITIE cin.tie(0)
#define OTIE cout.tie(0)
#define PY puts("Yes")
#define PN puts("No")
#define PW puts("-1")
#define P0 puts("0")
#define P__ puts("")
#define PU puts("--------------------")
#define mp make_pair
#define fi first
#define se second
#define gc getchar
#define pc putchar
#define pb emplace_back
#define un using namespace
#define il inline
#define all(x) x.begin(),x.end()
#define mem(x,y) memset(x,y,sizeof x)
#define popc __builtin_popcountll
#define rep(a,b,c) for(int a=(b);a<=(c);++a)
#define per(a,b,c) for(int a=(b);a>=(c);--a)
#define reprange(a,b,c,d) for(int a=(b);a<=(c);a+=(d))
#define perrange(a,b,c,d) for(int a=(b);a>=(c);a-=(d))
#define graph(i,j,k,l) for(int i=k[j];i;i=l[i].nxt)
#define lowbit(x) ((x)&-(x))
#define lson(x) ((x)<<1)
#define rson(x) ((x)<<1|1)
//#define double long double
//#define int long long
//#define int __int128
using namespace std;
un __gnu_pbds;
using i64=long long;
using u64=unsigned long long;
using pii=pair<int,int>;
template<typename T1,typename T2>inline void ckmx(T1 &x,T2 y){x=x>y?x:y;}
template<typename T1,typename T2>inline void ckmn(T1 &x,T2 y){x=x<y?x:y;}
inline auto rd(){
	int qwqx=0,qwqf=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')qwqf=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){qwqx=(qwqx<<1)+(qwqx<<3)+ch-48;ch=getchar();}return qwqx*qwqf;
}
template<typename T>inline void write(T qwqx,char ch='\n'){
	if(qwqx<0){qwqx=-qwqx;putchar('-');}
	int qwqy=0;static char qwqz[40];
	while(qwqx||!qwqy){qwqz[qwqy++]=qwqx%10+48;qwqx/=10;}
	while(qwqy--){putchar(qwqz[qwqy]);}if(ch)putchar(ch);
}
bool Mbg;
const int mod=998244353;
template<typename T1,typename T2>inline void adder(T1 &x,T2 y){x+=y,x=x>=mod?x-mod:x;}
template<typename T1,typename T2>inline void suber(T1 &x,T2 y){x-=y,x=x<0?x+mod:x;}
const int maxn=2e5+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,m,Q;
vector<int>G[maxn];
int dfn[maxn],low[maxn],dfncnt,sta[maxn],tp,sc;
vector<int>T[maxn];
void tar(int x,int y){
	dfn[x]=low[x]=++dfncnt,sta[++tp]=x;
	for(int u:G[x])if(u^y){
		if(!dfn[u]){
			tar(u,x),ckmn(low[x],low[u]);
			if(low[u]>=dfn[x]){
				int z;++sc;
				do{z=sta[tp--],T[sc+n].pb(z),T[z].pb(sc+n);}while(z^u);
				T[sc+n].pb(x),T[x].pb(sc+n);
			}
		}else ckmn(low[x],dfn[u]);
	}
}
int fa[maxn][20],siz[maxn],dep[maxn];
void dfs0(int x,int y){
	dfn[x]=++dfncnt,siz[x]=1,fa[x][0]=y,dep[x]=dep[y]+1;
	rep(i,1,16)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int u:T[x])if(u^y)dfs0(u,x),siz[x]+=siz[u];
}
bool f[maxn],g[maxn];
//方点 f fa[i]对i子树的答案 
gp_hash_table<int,bool>e[maxn];
void dfs1(int x,int y){
	f[x]=1;
	for(int u:T[x])if(u^y){
		dfs1(u,x),f[x]&=f[u];
		if(x>n)f[x]&=e[y][u];
	}
}
void dfs2(int x,int y){
	if(x<=n){
		int z=!g[x];
		for(int u:T[x])if(u^y){
			if(!f[u])++z;
		}
		for(int u:T[x])if(u^y){
			g[u]=!(z-(!f[u]));
			dfs2(u,x);
		}
	}else{
		int z=!g[x];
		for(int u:T[x])if(u^y){
			if(!f[u])++z;
		}
		for(int u:T[x])if(u^y){
			bool r=!(z-(!f[u]));
			if(r){
				for(int v:T[x])if(u^v){
					if(!e[u][v]){
						r=0;break;
					}
				}
				g[u]=r;
			}else{
				g[u]=0;
			}
			dfs2(u,x);
		}
	}
}
inline void solve_the_problem(){
	n=rd(),m=rd(),Q=rd();
	rep(i,1,m){
		int x=rd(),y=rd();
		G[x].pb(y),G[y].pb(x);
		e[x][y]=e[y][x]=1;
	}
	tar(1,0);
	dfncnt=0;
	dfs0(1,0);
	dfs1(1,0);
	g[1]=1;
	dfs2(1,0);
//	rep(i,1,n)write(f[i],32);P__;
//	rep(i,1,n)write(g[i],32);P__;
	rep(i,1,n)if(f[i]&&g[i]){
		rep(_,1,Q)PY;
		return;
	}
	while(Q--){
		int x=rd(),y=rd();
		if(dfn[x]<=dfn[y]&&dfn[y]<dfn[x]+siz[x]){
			int p=dep[y]-dep[x]-1;
			per(i,16,0)if((p>>i)&1)y=fa[y][i];
			f[y]?PY:PN;
		}else{
			g[x]?PY:PN;
		}
	}
}
bool Med;
signed main(){
//	freopen(".in","r",stdin);freopen(".out","w",stdout);
	fprintf(stderr,"%.3lfMB\n",(&Mbg-&Med)/1048576.0);
	int _=1;
	while(_--)solve_the_problem();
}
/*
6 8 1
1 2
2 3
3 1
1 4
1 6
4 5
5 6
4 6
1 4
*/

posted @ 2025-07-31 09:01  dcytrl  阅读(40)  评论(0)    收藏  举报