​题面在这里!​

 

    填一下APIO时候的坑。。。

    其实就是圆方树的裸题。。。我们只要把圆方树建出来之后,分两种情况讨论一下就行了:不走回头路 和 只在某个方点(代表双联通分量的点)经过两次 的方案。。。

 



#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int N=100005,M=400005;

inline int read(){
int x=0; char ch=getchar();
for(;!isdigit(ch);ch=getchar());
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return x;
}

vector<int> g[N*2];
int hd[N],to[M],ne[M],num,n,m,siz[N*2];
int v[N],cnt,dc,dfn[N],low[N],nn;
ll ans=0;

struct lines{ int x,y;};
stack<lines> S;

inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;}

void tarjan(int x,int fa){
dfn[x]=low[x]=++dc,nn++;
for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa)
if(!dfn[to[i]]){
S.push((lines){x,to[i]});
tarjan(to[i],x),low[x]=min(low[x],low[to[i]]);

if(low[to[i]]>=dfn[x]){
cnt++; lines e;
for(e=S.top(),S.pop();;e=S.top(),S.pop()){
if(v[e.x]!=cnt) v[e.x]=cnt,g[cnt].pb(e.x),g[e.x].pb(cnt);
if(v[e.y]!=cnt) v[e.y]=cnt,g[cnt].pb(e.y),g[e.y].pb(cnt);

if(e.x==x&&e.y==to[i]) break;
}
}
}
else if(dfn[to[i]]<low[x]){
S.push((lines){x,to[i]});
low[x]=dfn[to[i]];
}
}

void dfs(int x,int fa){
if(x<=n){
for(int i:g[x]) if(i!=fa)
dfs(i,x),ans+=siz[x]*(ll)siz[i],siz[x]+=siz[i];
ans+=siz[x]*(ll)(nn-siz[x]-1);
siz[x]++;
}
else{
int sz=g[x].size()-2;
for(int i:g[x]) if(i!=fa)
dfs(i,x),ans+=siz[x]*(ll)siz[i]*(ll)sz,siz[x]+=siz[i];
ans+=siz[x]*(ll)(nn-siz[x])*(ll)sz;
}
}

inline void solve(){
for(int i=1;i<=n;i++) if(!dfn[i]){
nn=0,tarjan(i,0);
dfs(i,0);
}
}

int main(){
n=read(),m=read(),cnt=n;
for(int i=1,uu,vv;i<=m;i++){
uu=read(),vv=read();
add(uu,vv),add(vv,uu);
}

solve();

printf("%lld\n",ans<<1);
return 0;
}


 


我爱学习,学习使我快乐