【圆方树+树形dp】P4630 [APIO2018] Duathlon 铁人两项
题面:https://www.luogu.com.cn/problem/P4630
先用tarjan把原图建成圆方树
圆点赋值-1,方点赋值环的大小
对于每个点对,方案数为圆方树上的路径权值
然后利用树形dp处理每个点对的贡献
计算子树根节点经过的次数乘以2即可
代码
#include<bits/stdc++.h> using namespace std; const int maxn=2e5+5; int n,m,head[maxn],size[maxn],cnt,tot,tott,v[maxn],h[maxn<<1],low[maxn],dfn[maxn],s[maxn],times,top; struct edge { int to,nxt; }e[maxn<<1],G[maxn<<3]; void add(int x,int y) { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } void addd(int x,int y) { G[++tott].nxt=h[x]; G[tott].to=y; h[x]=tott; } void tarjan(int x) { low[x]=dfn[x]=++times; s[++top]=x; size[x]=1; for(int i=head[x];i;i=e[i].nxt) { int to=e[i].to; if(!dfn[to]) { tarjan(to); low[x]=min(low[x],low[to]); if(low[to]==dfn[x]) { int xx=0,num=1; tot++; do { xx=s[top--]; ++num; addd(tot,xx); addd(xx,tot); size[tot]+=size[xx]; }while(xx!=to); v[tot]=num; size[x]+=size[tot]; addd(x,tot); addd(tot,x); } } else low[x]=min(low[x],dfn[to]); } } long long ans=0; void dfs(int root,int u,int fa) { int tmp=(u<=n); ans+=2LL*size[u]*(size[root]-size[u])*v[u]; for(int i=h[u];i;i=G[i].nxt) { int to=G[i].to; if(to==fa) continue; ans+=2LL*tmp*size[to]*v[u]; tmp+=size[to]; dfs(root,to,u); } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); scanf("%d%d",&n,&m); int x,y; tot=n; for(int i=1;i<=n;i++) v[i]=-1; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) { tarjan(i); dfs(i,i,0); } printf("%lld",ans); return 0; }