删边(cip)

 删边(cip)

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

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


Sol

神题,无从下手啊。

考虑点dfs建出dfs树,边分为两种--树边,非树边。

那么割断两条非树边显然不行。

考虑割一条树边a和一条非树边b,当b为a子树内唯一返祖边或a子树无返祖边时不行。

考虑两条树边ab,我们把一条返祖边打在它覆盖的所有树边上,如果这两条非树边被覆盖的集合相同,那么他们中间的那一段就会断开,就可以。

于是可以把每条返祖边给个hash值,开始处加,结束处减,统计每条边的覆盖集合,在排序统计下就行。

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 300005
#define rand() ((rand()<<15)|rand())
#define ll unsigned long long
using namespace std;
int n,m,head[maxn],tot,flag[maxn],t;
int sum[maxn],sz[maxn],a[maxn],deep[maxn];
struct node{
    int v,nex;int h;
}e[maxn*2];
ll ans,ha[maxn];
void add(int t1,int t2){
    e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void dfs(int k,int fa){
    flag[k]=1; deep[k]=deep[fa]+1;
    for(int i=head[k];i;i=e[i].nex){
        if(flag[e[i].v]){
            if(e[i].v!=fa&&deep[e[i].v]<deep[k]){
                ha[e[i].v]-=e[i].h;
                ha[k]+=e[i].h;
                sz[k]++;sz[e[i].v]--;
            }
            continue;
        }
        dfs(e[i].v,k);
    }
}
void tj(int k){
    flag[k]=1;
    for(int i=head[k];i;i=e[i].nex){
        if(flag[e[i].v])continue;
        tj(e[i].v);
        sz[k]+=sz[e[i].v];
        ha[k]+=ha[e[i].v];
    }
    if(k!=1){
        if(!sz[k])ans+=m-1,t++;
        if(sz[k]==1)ans++;
    }
}
int main(){
    srand(23333);
    cin>>n>>m;
    for(int i=1,t1,t2;i<=m;i++){
        scanf("%d%d",&t1,&t2);
        add(t1,t2);add(t2,t1);
        e[tot].h=e[tot-1].h=rand()*rand();
    }
    dfs(1,0);
    memset(flag,0,sizeof flag);tj(1);
    sort(ha+1,ha+n+1);
    for(int i=1;i<=n;i++){
        int j=i;
        for(;ha[j+1]==ha[i];j++);
        if(ha[j]==0)continue;
        int len=j-i+1;
        ans=ans+1LL*len*(len-1)/2;
        i=j;
    }
    ans=ans-1LL*t*(t-1)/2;
    cout<<ans<<endl;
    return 0;
}
View Code

 

posted @ 2019-04-12 15:06  liankewei123456  阅读(378)  评论(0编辑  收藏  举报