do_while_true

一言(ヒトコト)

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

[HNOI 2019] 校园旅行

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

\(1\leq n\leq 5000,1 \leq m \leq 5\times 10^5,1 \leq q \leq 10^6\)

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

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

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

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

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

这样子建图之后做那个 bfs 就可以 \(\mathcal{O}(n^2+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 @ 2022-01-17 22:02  do_while_true  阅读(40)  评论(1编辑  收藏  举报