[HNOI2019] 校园旅行

题意:

给定一张n个点m条边的无向图,无重边无自环,每个点有一个权值0或1。

有q组询问,每次询问两个点$(u,v)$之间是否存在一条路径$u\rightarrow v$,满足其依次经过的点权组成的01串为回文串,路径不一定是简单路径。

$n\leq 5000,m\leq 5\times 10^{5},q\leq 10^{5}$。

 

题解:

首先有一个$O(m^{2})$的暴力大家都会,用队列维护合法的二元组,按类似于spfa的方法依次松弛即可。

发现好像没有什么本质不同的做法,所以考虑优化。

这个做法的问题在于边数太多,如果能删掉一些不影响答案的边应该就可以了。

发现边分三类:0到0,1到1,0到1,且在回文串中每类互不影响,所以我们可以分开考虑这三类边。

发现重复走一个点不会改变路径长度的奇偶性,所以答案应该只和路径长度奇偶性有关,因为可以在一个点绕掉数量差。

以上三条线索拼在一起加上人类智慧就能构造出一个做法(根本不会啊,啥玩意啊):

将三类边依次单拿出来连,对于一个联通块:

  • 如果它是二分图,那么把它改成它的一棵生成树不会对路径长度奇偶性产生任何影响,可以去掉其他边。
  • 如果不是二分图,那么在某个点加个自环就能起到调换奇偶性的作用,依然可以只保留一棵生成树。

由于每类边之间互不影响,可以证明删完之后原来合法的现在还合法(在一个点绕),原来不合法的现在还不合法。

这样边数就与n同级了,复杂度也从$O(m^{2})$变为了$O(n^{2})$。

神仙图论题我就没做出来过,zbl

 

套路:

  • 路径长度奇偶性相关$\rightarrow$二分图。
  • 可以将边分成互不干扰的几类$\rightarrow$分层图。

 

代码:

#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\n");
        else printf("NO\n");
    }
    return 0;
}
校园旅行

 

posted @ 2020-06-16 14:13  Fugtemypt  阅读(187)  评论(0编辑  收藏  举报