Problem
给定一个有n个点m条边的无向连通图,可以删掉两条边,问有多少种方案使得这个连通图不连通
1≤n≤105,1≤m≤3×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;
}