「题解」洛谷 P5292 [HNOI2019]校园旅行

「题解」洛谷 P5292 [HNOI2019]校园旅行

[HNOI 2019] 校园旅行

给定无向图 G=(V,E),每个点有 01 的一个标记,有 q 组询问,每组询问给定 s,tV,你需要求出是否存在一条 st 的路径 P,使得路径经过的点的标记拼成一个回文。P 可以不是简单路径。

1n5000,1m5×105,1q106

暴力的想法是考虑 fi,ji 是否能走到 j,然后用类似 bfs 的方法更新 f,每次从队列中取出一个 f,往两边扩展,如果能更新新的 f 就扔进队列里面。这样复杂度是 deg×deg=O(m2).想办法优化这个暴力,也就是减少无用的边数。

如果要经过 (u,v) 从点 u 走到点 v,考虑可以在这条边上来回折返,那么我们只关心这两个点组成的 01 串出现次数的奇偶性。

考虑仅有两端点为 00 或者 11,10(01) 的边及其端点组成的连通块,假设为 00:如果要经过这个连通块,由于只关心路径上 00 出现次数的奇偶性,所以要保留的信息仅为任意两点之间路径经过边数奇偶性的可能性。

考虑到如果连通块中有奇环,那么在这个奇环中走一圈会到起点,即可改变路径奇偶性,则任意两点经过的边数奇偶都可以;如果没有奇环,说明是个二分图,那么两点如果在同一部点中,则经过边数一定是偶数,否则经过边数一定是奇数。

由于我们只关心经过路径奇偶性,所以如果该连通分量是二分图,那么直接保留一个尽量小的连通子图,保留一棵生成树即可,如果该连通分量不是二分图,保留一棵生成树然后加一个非树边组成一个奇环即可,例如一个自环。可以发现这样边数降到了 O(n),任意两点之间路径经过边数奇偶性的可能性不变。

这样子建图之后做那个 bfs 就可以 O(n2+q) 解决这个问题。

#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue> 
#define pb emplace_back
#define mp std::make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
typedef std::pair<int,int>pii;
typedef std::pair<ll,int>pli;
typedef std::pair<ll,ll>pll;
typedef std::vector<int>vi;
typedef std::vector<pii>vpii;
template<typename T>T cmax(T &x, T y){ return x=x>y?x:y; }
template<typename T>T cmin(T &x, T y){ return x=x<y?x:y; }
template<typename T>
T &read(T &r){
	r=0;bool w=0;char ch=getchar();
	while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
	while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
	return r=w?-r:r;
}
template<typename T1,typename... T2>
void read(T1 &x, T2& ...y){ read(x); read(y...); }
const int N=5010;
const int M=500010;
int n,m,Q,u[M],v[M],dep[N];
char c[N];
int vv[M],ve[M],fl,f[N][N];
vi eg[N];
vpii e[N][3];
queue<pii>q;
void dfs(int x,int f,int t){
	vv[x]=t;dep[x]=dep[f]^1;
	for(auto y:e[x][t])if(y.fi!=f){
		ve[y.se]=1;
		if(vv[y.fi]==t){
			if(dep[x]==dep[y.fi])fl=1;
			continue;
		}
		eg[x].pb(y.fi);eg[y.fi].pb(x);
		dfs(y.fi,x,t);
	}
}
signed main(){
	read(n,m,Q);
	scanf("%s",c+1);
	for(int i=1;i<=n;i++)c[i]-='0',vv[i]=-1;
	for(int i=1;i<=m;i++){
		read(u[i],v[i]);
		e[u[i]][c[u[i]]+c[v[i]]].pb(mp(v[i],i));
		e[v[i]][c[u[i]]+c[v[i]]].pb(mp(u[i],i));
	}
	for(int i=1;i<=m;i++)
		if(!ve[i]){
			fl=0;
			dfs(u[i],0,c[u[i]]+c[v[i]]);
			if(fl)eg[u[i]].pb(u[i]);
		}
	for(int i=1;i<=m;i++)
		if(c[u[i]]==c[v[i]])
			f[v[i]][u[i]]=f[u[i]][v[i]]=1,q.push(mp(u[i],v[i]));
	for(int i=1;i<=n;i++)
		f[i][i]=1,q.push(mp(i,i));
	while(!q.empty()){
		int x=q.front().fi,y=q.front().se;q.pop();
		for(auto s:eg[x])
			for(auto t:eg[y])
				if(!f[s][t]&&c[s]==c[t])
					f[t][s]=f[s][t]=1,q.push(mp(s,t));
	}
	while(Q--){
		int x,y;read(x,y);
		puts(f[x][y]?"YES":"NO");
	}
	return 0;
}
posted @   do_while_true  阅读(48)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?

This blog has running: 1845 days 1 hours 33 minutes 22 seconds

点击右上角即可分享
微信分享提示