题意:
给定一张n个点m条边的无向图,无重边无自环,每个点有一个权值0或1。
有q组询问,每次询问两个点$(u,v)$之间是否存在一条路径$u ightarrow v$,满足其依次经过的点权组成的01串为回文串,路径不一定是简单路径。
$nleq 5000,mleq 5 imes 10^{5},qleq 10^{5}$。
题解:
首先有一个$O(m^{2})$的暴力大家都会,用队列维护合法的二元组,按类似于spfa的方法依次松弛即可。
发现好像没有什么本质不同的做法,所以考虑优化。
这个做法的问题在于边数太多,如果能删掉一些不影响答案的边应该就可以了。
发现边分三类:0到0,1到1,0到1,且在回文串中每类互不影响,所以我们可以分开考虑这三类边。
发现重复走一个点不会改变路径长度的奇偶性,所以答案应该只和路径长度奇偶性有关,因为可以在一个点绕掉数量差。
以上三条线索拼在一起加上人类智慧就能构造出一个做法(根本不会啊,啥玩意啊):
将三类边依次单拿出来连,对于一个联通块:
- 如果它是二分图,那么把它改成它的一棵生成树不会对路径长度奇偶性产生任何影响,可以去掉其他边。
- 如果不是二分图,那么在某个点加个自环就能起到调换奇偶性的作用,依然可以只保留一棵生成树。
由于每类边之间互不影响,可以证明删完之后原来合法的现在还合法(在一个点绕),原来不合法的现在还不合法。
这样边数就与n同级了,复杂度也从$O(m^{2})$变为了$O(n^{2})$。
(神仙图论题我就没做出来过,zbl)
套路:
- 路径长度奇偶性相关$ ightarrow$二分图。
- 可以将边分成互不干扰的几类$ ightarrow$分层图。
代码:
#include<bits/stdc++.h> #define maxn 5005 #define maxm 1000005 #define inf 0x7fffffff #define ll long long #define mp make_pair #define fi first #define se second #define pii pair<int,int> #define rint register int #define debug(x) cerr<<#x<<": "<<x<<endl #define fgx cerr<<"--------------"<<endl #define dgx cerr<<"=============="<<endl using namespace std; int to[maxm],nxt[maxm],hd[maxn],cnt; int to1[maxm],nxt1[maxm],hd1[maxn],cnt1; int tot[3],vis[maxn],col[maxn],ok[maxn][maxn]; char S[maxn]; queue<pii> Q; pii E[maxm][3]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } inline void addedge(int u,int v){ //cout<<u<<":"<<v<<endl; if(S[u]==S[v]) ok[u][v]=ok[v][u]=1,Q.push(mp(u,v)); to[++cnt]=v,nxt[cnt]=hd[u],hd[u]=cnt; to[++cnt]=u,nxt[cnt]=hd[v],hd[v]=cnt; } inline void addedge1(int u,int v){ //cout<<u<<"::"<<v<<endl; to1[++cnt1]=v,nxt1[cnt1]=hd1[u],hd1[u]=cnt1; to1[++cnt1]=u,nxt1[cnt1]=hd1[v],hd1[v]=cnt1; } inline void solve(int n){ while(!Q.empty()){ int u=Q.front().first,v=Q.front().second; Q.pop(); for(int i=hd[u];i;i=nxt[i]) for(int j=hd[v];j;j=nxt[j]){ int tu=to[i],tv=to[j]; if(S[tu]!=S[tv] || ok[tu][tv]) continue; ok[tu][tv]=ok[tv][tu]=1,Q.push(mp(tu,tv)); } } } inline int dfs(int u,int c){ //cout<<u<<endl; vis[u]=1,col[u]=c; int res=1; for(int i=hd1[u];i;i=nxt1[i]){ int v=to1[i]; if(!vis[v]) addedge(u,v),res=min(res,dfs(v,c^1)); else if(col[v]==col[u]) res=0; } return res; } int main(){ int n=read(),m=read(),q=read(); scanf("%s",S+1); for(int i=1;i<=m;i++){ int u=read(),v=read(); if(S[u]==S[v] && S[u]=='0') E[++tot[0]][0]=mp(u,v); if(S[u]==S[v] && S[u]=='1') E[++tot[1]][1]=mp(u,v); if(S[u]!=S[v]) E[++tot[2]][2]=mp(u,v); } for(int i=1;i<=n;i++) ok[i][i]=1,Q.push(mp(i,i)); for(int k=0;k<3;k++){ //debug(k); memset(hd1,0,sizeof(hd1)),cnt1=0; memset(vis,0,sizeof(vis)); memset(col,-1,sizeof(col)); for(int i=1;i<=tot[k];i++) addedge1(E[i][k].fi,E[i][k].se); for(int i=1;i<=n;i++) if(hd1[i] && !vis[i]) if(!dfs(i,0)) addedge(i,i); } solve(n); while(q--){ int u=read(),v=read(); if(ok[u][v]) printf("YES "); else printf("NO "); } return 0; }