[BZOJ4435][Cerc2015]Juice Junctions
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;
}