原文链接:https://blog.csdn.net/a_forever_dream/java/article/details/103060734
有一张图,每个点至多连三条边,每条边流量上限都是 1,设 f(x,y) 表示 x到 y 的最大流量,那么要你求出 、
所有不同的两个点对的流量之和.
input
6 8
1 3
2 3
4 1
5 6
2 6
5 1
6 4
5 3
output
36
因为每个点至多连三条边,所以 f(x,y) f(x,y)f(x,y) 的值只可能是 0,1,2,3 0,1,2,30,1,2,3。
如果两点间不连通,那么就是 0
如果两点不在一个边双里面但是连通,那么这两点间的路径显然有割点,所以就是 1
那么怎么判断 2和 3呢?
如果是 2 的话,那么说明两点间只有两条边不重复路径,那么如果删掉路径上的一条边,那么这两个点就不属于同一个边双了。
所以我们可以尝试删掉图中的每一条边,然后每次都求一次边双,假如两个点一直存在于同一个边双,那么就是 3,否则是 2。
怎么判断呢?我们可以将一个点呆过的所有边双的编号用哈希压成一个数,然后判断两点的哈希值是否相等即可。
#include <cstdio> #include <cstring> #define maxn 3010 int n,m; struct edge{int y,next;}; edge e[maxn<<2]; int first[maxn],len=1; void buildroad(int x,int y) { e[++len]=(edge){y,first[x]}; first[x]=len; } int dfn[maxn],low[maxn],belong[maxn],block[maxn],id=0,cnt=0; int zhan[maxn],t=0; void dfs(int x,int from,int edg) { dfn[x]=low[x]=++id;zhan[++t]=x; for(int i=first[x];i;i=e[i].next) { if(i==(from^1)||i==edg||i==(edg^1))continue; int y=e[i].y; if(!dfn[y]) { if(edg==-1)block[y]=block[x]; dfs(y,i,edg); if(low[y]<low[x])low[x]=low[y]; } else if(dfn[y]<low[x])low[x]=dfn[y]; } if(dfn[x]==low[x]) { cnt++; int xx; do{ xx=zhan[t--]; belong[xx]=cnt; }while(xx!=x); } } void tarjan(int edg=0) { memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(belong,0,sizeof(belong));id=cnt=0; for(int i=1;i<=n;i++) if(dfn[i]==0)dfs(i,-1,edg); } int ans[maxn][maxn]; void work1() { for(int i=1;i<=n;i++) if(dfn[i]==0)block[i]=i,dfs(i,-1,-1); for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) if(block[i]!=block[j])ans[i][j]=0; else if(belong[i]!=belong[j])ans[i][j]=1; } int hash[maxn]; #define mod 100000007 void work2() { for(int i=2;i<=len;i+=2) { tarjan(i); for(int j=1;j<=n;j++) hash[j]=(hash[j]*13+belong[j])%mod; } for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) if(ans[i][j]==-1)ans[i][j]=hash[i]==hash[j]?3:2; } void print() { int tot=0; for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) tot+=ans[i][j]; printf("%d",tot); } int main() { scanf("%d %d",&n,&m); for(int i=1,x,y;i<=m;i++) scanf("%d %d",&x,&y),buildroad(x,y),buildroad(y,x); memset(ans,-1,sizeof(ans)); work1(); work2(); print(); }
#include<iostream> #include<cstdio> #include<cstring> #define ll unsigned long long using namespace std; inline int read() { int r,s=0,c; for(;!isdigit(c=getchar());s=c); for(r=c^48;isdigit(c=getchar());(r*=10)+=c^48); return s^45?r:-r; } const int N=3010,M=4510; const ll p=19491001; int n,m; struct edge { int to,next; }mem[M<<1]; int head[N],cnt=1; inline void add(int u,int v) { mem[++cnt].next=head[u]; mem[cnt].to=v; head[u]=cnt; } int fa[N],f1,f2; inline int getfa(int x) { if(fa[x]!=x)fa[x]=getfa(fa[x]); return fa[x]; } int dfn[N],low[N],ti; int st[N],top; int dcc[N],co; bool vis[M<<1]; ll d[N]; void tarjan(int u,int no) { low[u]=dfn[u]=++ti; st[++top]=u; for(int i=head[u];i>0;i=mem[i].next) { if(vis[i])continue; if((i==no)||((i^1)==no))continue; int to=mem[i].to; if(!dfn[to]) { vis[i]=vis[i^1]=1; tarjan(to,no); vis[i]=vis[i^1]=0; low[u]=min(low[u],low[to]); } else low[u]=min(low[u],dfn[to]); } if(low[u]==dfn[u]) { dcc[u]=++co; while(st[top]!=u) dcc[st[top--]]=co; top--; } } inline void work() { for(int i=1;i<=n;i++) d[i]=1ll; for(int no=1;no<=m+1;no++) { memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); ti=0,co=0,top=0; for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,no<<1); for(int i=1;i<=n;i++) d[i]=d[i]*p+(ll)dcc[i]; } } inline int calc(int u,int v) { f1=getfa(u),f2=getfa(v); if(f1!=f2)//不在一个集合内最大流为0 return 0; if(dcc[u]!=dcc[v])//不在一个dcc内最大流为1 return 1; if(d[u]==d[v])//用hash判断每次的dcc是否完全相同 return 3;/*如果删掉任意一条边,u,v仍在同一个dcc内 则u,v在同一个三联通分量内,即最大流为3*/ return 2;//否则最大流为2 } int main() { n=read(),m=read(); for(int i=1;i<=n;i++) fa[i]=i; int u,v; for(int i=1;i<=m;i++) { u=read(),v=read(); add(u,v),add(v,u); f1=getfa(u),f2=getfa(v); if(f1!=f2)fa[f1]=f2; } work(); int ans=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) ans+=calc(i,j); printf("%d ",ans); return 0; }