[APIO2018] Duathlon 铁人两项
填一下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; }
我爱学习,学习使我快乐