洛谷传送门
BZOJ传送门
解析:
对所有点双建立圆方树,设圆点权值为 − 1 -1 −1,方点权值为点双大小。那么原题转化为求树上所有圆点两两路径的权值和,直接上树形DP。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int N=4e5+5;
int n,tot,m;
struct Graph{
int to[N],nxt[N],last[N],ecnt;
void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
}
}g1,g2;
int dfn[N],low[N],dfs_clock;
int sta[N],top;
int val[N],siz[N],sum;
void tarjan(int u){
dfn[u]=low[u]=++dfs_clock;
sta[++top]=u;
siz[u]=1;val[u]=-1;
for(int re e=g1.last[u],v=g1.to[e];e;v=g1.to[e=g1.nxt[e]]){
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
g2.addedge(u,++tot);
val[tot]=1;
int x;
do{
x=sta[top--];
g2.addedge(tot,x);
siz[tot]+=siz[x];
++val[tot];
}while(x!=v);
siz[u]+=siz[tot];
}
}
else low[u]=min(low[u],dfn[v]);
}
}
ll ans;
void dfs(int u){
if(u<=n)ans+=(ll)(sum-1)*val[u];
ans+=(ll)(sum-siz[u])*siz[u]*val[u];
for(int re e=g2.last[u],v=g2.to[e];e;v=g2.to[e=g2.nxt[e]]){
ans+=(ll)(sum-siz[v])*siz[v]*val[u];
dfs(v);
}
}
signed main(){
tot=n=getint(),m=getint();
for(int re i=1;i<=m;++i){
int u=getint(),v=getint();
g1.addedge(u,v);
g1.addedge(v,u);
}
for(int re i=1;i<=n;++i)if(!dfn[i])tarjan(i),sum=siz[i],dfs(i);
cout<<ans;
return 0;
}