题意:有一个n个点m条边的无向图,需要任意割两条边使图不连通。求方案数。
假设我们有了图的任意一个生成树,易知答案一共由三个部分贡献
- 如果某两个点只被某两条边同时覆盖,那么同时删去这两条边;
- 如果任意一条由根出发的路径上,如果任意两条树边它们被其他边覆盖情况是一样的,那么我们可以把这两条树边删去中间部分就会变得与原图不连通;
- 桥+任意一条边。
其中1和3只要随便做一次树上差分跑个dfs就可以统计贡献。难点是我们如何维护任意树边被其他边的覆盖情况。
我们可以对路径hash
- 像查分一样在路径两端+-上一个随机值,或者同时亦或上一个随机值(推荐使用后者,加和太容易冲突了)。
- 两个树边的权值如果相同那么它们的被其他边覆盖的情况是一样的。
把上述情况全部加起来就是最终结果了。
#include<algorithm>
#include<vector>
#include<iostream>
#include<math.h>
#include<cstring>
#include<string>
#include<stack>
#include<map>
#include<set>
#include<unordered_map>
#include<queue>
#include <assert.h>
#define qcin; ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define pb push_back
#define mp make_pair
#define clr(x) memset(x,0,sizeof x)
#define fmax(x) memset(x,0x3f,sizeof x)
#define finit(x) memset(x,-1,sizeof x)
#define dis(l,r) r-l+1
#define gstr(str) scanf("%s",str)
#define glen(str) strlen(str)
using namespace std;
typedef long long ll;
namespace Input
{
const int BUF = 65536;
char buf[BUF + 1];
char *head = buf, *tail = buf;
}
inline char inputchar()
{
using namespace Input;
if(head == tail)
*(tail = (head = buf) + fread(buf, 1, BUF, stdin)) = 0;
return *head++;
}
inline void io(int &ret){
char ch = inputchar();
while(ch < '0' || ch > '9')
ch = inputchar();
ret = ch - '0';
ch = inputchar();
while(ch >= '0' && ch <= '9'){
ret = ret * 10 + ch - '0';
ch = inputchar();
}
}
inline void sio(int &ret){
ret = 0;
char ch = inputchar();
while((ch < '0' || ch > '9') && ch != '-')
ch = inputchar();
bool neg = false;
if(ch == '-')
neg = true, ch = inputchar();
while(ch >= '0' && ch <= '9')
{
ret = ret * 10 + ch - '0';
ch = inputchar();
}
if(neg)
ret = -ret;
}
typedef pair<ll,ll>pll;
const int maxn = 5e5+10;
const ll mo = 2148473648;
typedef ll arr[maxn];
typedef char str[maxn];
void file(int x){if(x&&fopen("in.txt","r")){freopen("in.txt","r",stdin);if(x==2)freopen("1.txt","w",stdout);}}
struct Edge{
int to,cst,nxt;
}E[maxn*10];
const double pi=acos(-1);
arr dfn,id,h,fa,dep,tree,num,hsh,a;
int n,tot,pos,m,st,cnt,bri;
void addedge(ll h[],int u,int v,int w=0){
E[tot].to=v;
E[tot].cst=w;
E[tot].nxt=h[u];
h[u]=tot++;
}
struct node {
int u,v;
}e[maxn];
void dfs(int x,int d){
dfn[x]=++pos,id[pos]=x;dep[x]=d;
for(int i=h[x];~i;i=E[i].nxt){
int y=E[i].to;
if(!dfn[y]){
tree[i>>1]=1;
fa[y]=x;
dfs(y,d+1);
}
}
}
void init(){
clr(dfn);clr(tree);
clr(num);clr(hsh);
finit(h);
tot=0;pos=0;
cnt=0;bri=0;
}
unsigned long long last=0;
ll gethash(){
last=(rand()*rand()<<2)+rand()+last;
ll res=(last>>1);
return res;
}
int main(){
file(1);
srand(time(NULL));
int u,v,w;
io(n),io(m);
init();
for(int i=0;i<m;i++){
io(u),io(v);
addedge(h,u,v);
addedge(h,v,u);
e[cnt++]={u,v};
}
dfs(1,1);
for(int i=0;i<m;i++){
if(tree[i])continue;
int u=e[i].u;
int v=e[i].v;
if(dep[u]>dep[v])swap(u,v);
ll w=gethash();
assert(w>=0);
num[u]--;hsh[u]^=w;
num[v]++;hsh[v]^=w;
}
ll now=0,ans=0;
for(int i=pos;i>1;i--){
int p=id[i];
num[fa[p]]+=num[p];
hsh[fa[p]]^=hsh[p];
if(!num[p])bri++;
else {
ans+=(num[p]==1);
a[now++]=hsh[p];
}
}
sort(a,a+now);
ll tmp=1;
for(int i=1;i<now;i++){
if(a[i]==a[i-1])tmp++;
else{
ans+=tmp*(tmp-1)/2;
tmp=1;
}
}
ans+=tmp*(tmp-1)/2;
ans+=bri*(m-bri);
ans+=bri*(bri-1)/2;
printf("%lld\n",ans);
return 0;
}