[BZOJ4435][Cerc2015]Juice Junctions

bzoj
luogu

description

给你一张\(n\)\(m\)条边的无向图,保证每个点的度数不超过\(3\)。每条边容量为\(1\),求所有点对之间的最大流之和。
\(n\le3000,m\le4500\)

sol

据说这题可以用最小割树跑过去。。。\(\mbox{orz}\)
考虑一些靠谱一点的算法。因为最大流等于最小割,而一个点的度数不超过\(3\),所以任意两点之间的最大流应该\(\in\{0,1,2,3\}\)
对于所有最大流\(\in\{1,2,3\}\)的点对,它们满足连通。
对于所有最大流\(\in\{2,3\}\)的点对,它们满足在同一个边双连通分量中。
对于所有最大流\(=3\)的点对,它们满足什么?
满足任意删除一条边之后,它们仍在同一个边双连通分量中。
所以就每次删一条边然后跑边双,\(\mbox{hash}\)一下所处的边双连通分量的编号就好啦。
复杂度\(O(nm)\)

code

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi(){
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
#define ull unsigned long long
const int N = 3005;
const int base1 = 20011118;
const int base2 = 20020415;
int n,m,fa[N],to[N*3],nxt[N*3],head[N],cnt=1,dfn[N],low[N],tim,S[N],bel[N],scc,ban,ans;
ull hsh1[N],hsh2[N];
void link(int u,int v){
    to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void Tarjan(int u,int f){
    dfn[u]=low[u]=++tim;S[++S[0]]=u;
    for (int e=head[u];e;e=nxt[e]){
        if (to[e]==f||e==ban||e==(ban^1)) continue;
        if (!dfn[to[e]]) Tarjan(to[e],u),low[u]=min(low[u],low[to[e]]);
        else low[u]=min(low[u],dfn[to[e]]);
    }
    if (dfn[u]==low[u]){
        ++scc;int x=0;
        do x=S[S[0]--],bel[x]=scc; while (x!=u);
    }
}
int main(){
    n=gi();m=gi();
    for (int i=1;i<=n;++i) fa[i]=i;
    for (int i=1;i<=m;++i){
        int u=gi(),v=gi();
        link(u,v),link(v,u);
        fa[find(u)]=find(v);
    }
    for (int i=1;i<=n;++i) if (!dfn[i]) Tarjan(i,0);
    for (int i=1;i<=n;++i)
        for (int j=i+1;j<=n;++j)
            ans+=(find(i)==find(j))+(bel[i]==bel[j]);
    for (int i=1;i<=m;++i){
        memset(dfn,0,sizeof(dfn));tim=scc=0;
        ban=i<<1;
        for (int j=1;j<=n;++j) if (!dfn[j]) Tarjan(j,0);
        for (int j=1;j<=n;++j) hsh1[j]=hsh1[j]*base1+bel[j],hsh2[j]=hsh2[j]*base2+bel[j];
    }
    for (int i=1;i<=n;++i)
        for (int j=i+1;j<=n;++j)
            ans+=(hsh1[i]==hsh1[j]&&hsh2[i]==hsh2[j]);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zhoushuyu/p/9426493.html