Codechef TAPAIR

Problem

给定一个有n个点m条边的无向连通图,可以删掉两条边,问有多少种方案使得这个连通图不连通
1n105,1m3×105

Solution

随机大法走天下啊。。。
考虑图的dfs树,对于非树边,我们随机一个(0,2^64)的权值,然后对于一条树边,其权值为覆盖它的非树边的xor和,那么对于一个边集,如果我们可以割掉这个边集使得图不连通,那么其必有一个子集的xor和为0。题目要求删掉两条,就是说两条的权值相同,或其中一条为0,于是可以直接按照边权排序,直接计算即可。
由于我们是随机的,这使得出错的概率变得很小很小可以省略。
cc真的有鬼,我交了两个一样的代码一个对了一个错了。。。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>

#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)

using namespace std;

int get(){
    char ch;
    int s=0;
    bool bz=0;
    while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
    if (ch=='-')bz=1;else s=ch-'0';
    while(ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-'0';
    if (bz)return -s;
    return s;
}

typedef long long LL;

const int N = 1e+5+10;
const int M = 3e+5+10;

struct edge{
    int x,id,nxt;
}e[M*2];
bool bz[M],vis[N];
int h[N],tot,d[N],n,m,dep[N];
LL v[M];

void inse(int x,int y,int id){
    e[++tot].x=y;
    e[tot].id=id;
    e[tot].nxt=h[x];
    h[x]=tot;
}

void dfs(int x){
    vis[x]=1;
    for(int p=h[x];p;p=e[p].nxt)
    if (e[p].id!=d[x]){
        if (!vis[e[p].x]){
            d[e[p].x]=e[p].id;
            dep[e[p].x]=dep[x]+1;
            dfs(e[p].x);
            v[d[x]]^=v[d[e[p].x]];
        }
        else
            if (!bz[e[p].id]){
                bz[e[p].id]=1;
                v[e[p].id]=0;
                fo(i,1,21)v[e[p].id]=v[e[p].id]*8+rand()%8;
                v[d[x]]^=v[e[p].id];
            }
            else v[d[x]]^=v[e[p].id];
    }
    if (!d[x])v[0]=0;
}

int main(){
    srand(16789);
    n=get();m=get();
    fo(i,1,m){
        int x=get(),y=get();
        inse(x,y,i);
        inse(y,x,i);
    }
    dfs(1);
    sort(v+1,v+1+m);
    int w=m;
    fo(i,1,m)
    if (v[i]!=0){w=i-1;break;}
    LL ans=0;
    ans=LL(w)*(m-w)+LL(w)*(w-1)/2;
    int s=0;
    fo(i,w+1,m){
        if (v[i]!=v[i-1]){
            ans=ans+LL(s)*(s-1)/2;
            s=0;
        }
        s++;
    }
    ans=ans+LL(s)*(s-1)/2;
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值