题目大意: 有一张图,每个点至多连三条边,每条边流量上限都是 ,设 表示 到 的最大流量,那么要你求出 。
题解
因为每个点至多连三条边,所以 的值只可能是 。
如果两点间不连通,那么就是 。
如果两点不在一个边双里面但是连通,那么这两点间的路径显然有割点,所以就是 。
那么怎么判断 和 呢?
如果是 的话,那么说明两点间只有两条边不重复路径,那么如果删掉路径上的一条边,那么这两个点就不属于同一个边双了。
所以我们可以尝试删掉图中的每一条边,然后每次都求一次边双,假如两个点一直存在于同一个边双,那么就是 ,否则是 。
怎么判断呢?我们可以将一个点呆过的所有边双的编号用哈希压成一个数,然后判断两点的哈希值是否相等即可。
代码:
#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();
}