dtoj#4239. 删边(cip)

题目描述:

给出一个没有重边和自环的无向图,现在要求删除其中两条边,使得图仍然保持连通。

你的任务是计算有多少组不合法的选边方案。注意方案是无序二元组。

思路:

首先图分离成一棵生成树树和很多条返祖边

那么对于没有返祖边覆盖的边可以和任何一条边组合使整个图不连通。

我们叫原本就在树上的边为竖边,那么答案只有可能是一条竖边和一条返祖边或者两条竖边的组合。

然后这个时候我们发现对每条边有哪些返祖边覆盖开一个集合,当两条边所属集合完全相同,那么同时选择这两条边一定可以使图不联通。

现在我们考虑以后再那个很妙的方法,首先把边转移到点上,对于每条返祖边 $hash$ 一个权值,每次对于返祖边的两边节点同时异或上这个权值,那么这个点所在集合的异或值就等于它子树内所有节点的异或值。如果子树内同时包含这个两个节点,异或值抵消相当于没有计算到不覆盖我的返祖边的异或值。

 以下代码:

#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=1e5+5,M=3e5+5,p=1e9+7;
LL ans,val[M],v[N];
int n,m,head[N],ne[N<<1],to[N<<1],cnt,tot,fa[N],num;
struct node{
    int x,y;
}t[M];
il int read(){
    int x,f=1;char ch;
    _(!)ch=='-'?f=-1:f;x=ch^48;
    _()x=(x<<1)+(x<<3)+(ch^48);
    return f*x;
}
il void insert(int x,int y){
    ne[++cnt]=head[x];
    head[x]=cnt;to[cnt]=y;
}
il int getfa(int x){
    return (!fa[x])?x:(fa[x]=getfa(fa[x]));
}
il void dfs1(int x,int fa){
    for(int i=head[x];i;i=ne[i]){
        if(fa==to[i])continue;
        dfs1(to[i],x);v[x]^=v[to[i]];
    }
    if(x!=1)val[x-1]=v[x];
}
il LL rnd(){
    LL x=0;
    for(int k=0;k<3;k++)x=(x<<15)|rand();
    return x;
}
int main()
{
    srand(233);n=read();m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        int a=getfa(x),b=getfa(y);
        if(a==b)t[++tot]=(node){x,y};
        else{
            fa[a]=b;num++;
            insert(x,y);insert(y,x);
        }
    }
    if(num!=n-1)return puts("0"),0;
    for(int i=1;i<=tot;i++){
        int x=t[i].x,y=t[i].y;
        LL tmp=rnd();
        val[n+i-1]=tmp;
        v[x]^=tmp;v[y]^=tmp;
    }
    dfs1(1,0);
    sort(val+1,val+m+1);num=0;
    int l=1;while(!val[l]&&l<=m)l++,num++;
    ans+=1ll*num*(m-num)+1ll*(num-1)*num/2;num=0;
    for(int i=l;i<=m;i++){
        num++;
        if(val[i]!=val[i+1]){
            ans+=1ll*num*(num-1)/2;
            num=0;
        }
    }
    printf("%lld
",ans);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Jessie-/p/10520571.html