CERC 2015 Juice Junctions 题解

题目传送门

题目大意: 有一张图,每个点至多连三条边,每条边流量上限都是 1 1 ,设 f ( x , y ) f(x,y) 表示 x x y y 的最大流量,那么要你求出 x = 1 n y = x + 1 n f ( x , y ) \sum_{x=1}^n \sum_{y=x+1}^n f(x,y)

题解

因为每个点至多连三条边,所以 f ( x , y ) f(x,y) 的值只可能是 0 , 1 , 2 , 3 0,1,2,3

如果两点间不连通,那么就是 0 0

如果两点不在一个边双里面但是连通,那么这两点间的路径显然有割点,所以就是 1 1

那么怎么判断 2 2 3 3 呢?

如果是 2 2 的话,那么说明两点间只有两条边不重复路径,那么如果删掉路径上的一条边,那么这两个点就不属于同一个边双了。

所以我们可以尝试删掉图中的每一条边,然后每次都求一次边双,假如两个点一直存在于同一个边双,那么就是 3 3 ,否则是 2 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();
}
发布了234 篇原创文章 · 获赞 100 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/a_forever_dream/article/details/103060734