• [HNOI2019] 校园旅行


    题意:

    给定一张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;
    }
    校园旅行
  • 相关阅读:
    python之路----初识面向对象(二)
    python之路----初识面向对象
    python之路----包
    python之路----模块与序列化模块
    python之路----模块调用
    python之路----常用模块二
    Python之路----递归函数
    Python之路----内置函数
    【CSS】整屏大背景
    【PHP】打印输出var_dump+echo+print_r
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13140682.html
Copyright © 2020-2023  润新知