[HNOI 2019] 校园旅行
给定无向图 ,每个点有 或 的一个标记,有 组询问,每组询问给定 ,你需要求出是否存在一条 的路径 ,使得路径经过的点的标记拼成一个回文。 可以不是简单路径。
.
暴力的想法是考虑 为 是否能走到 ,然后用类似 bfs 的方法更新 ,每次从队列中取出一个 ,往两边扩展,如果能更新新的 就扔进队列里面。这样复杂度是 .想办法优化这个暴力,也就是减少无用的边数。
如果要经过 从点 走到点 ,考虑可以在这条边上来回折返,那么我们只关心这两个点组成的 串出现次数的奇偶性。
考虑仅有两端点为 或者 的边及其端点组成的连通块,假设为 :如果要经过这个连通块,由于只关心路径上 出现次数的奇偶性,所以要保留的信息仅为任意两点之间路径经过边数奇偶性的可能性。
考虑到如果连通块中有奇环,那么在这个奇环中走一圈会到起点,即可改变路径奇偶性,则任意两点经过的边数奇偶都可以;如果没有奇环,说明是个二分图,那么两点如果在同一部点中,则经过边数一定是偶数,否则经过边数一定是奇数。
由于我们只关心经过路径奇偶性,所以如果该连通分量是二分图,那么直接保留一个尽量小的连通子图,保留一棵生成树即可,如果该连通分量不是二分图,保留一棵生成树然后加一个非树边组成一个奇环即可,例如一个自环。可以发现这样边数降到了 ,任意两点之间路径经过边数奇偶性的可能性不变。
这样子建图之后做那个 bfs 就可以 解决这个问题。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?