CERC 2015 Juice Junctions


原文链接: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;
}

  

原文地址:https://www.cnblogs.com/cutemush/p/12688373.html