[圆方树] Luogu P4630 Duathlon 铁人两项
Posted comfortable
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[圆方树] Luogu P4630 Duathlon 铁人两项相关的知识,希望对你有一定的参考价值。
题目描述
比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成。
最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。
比赛的路线要按照如下方法规划:
- 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
- 选择一条从 ss出发,经过 cc最终到达 ff的路径。考虑到安全因素,选择的路径经过同一个点至多一次。
在规划路径之前,镇长想请你帮忙计算,总共有多少种不同的选取 s, cs,c和 ff的方案,使得在第 22步中至少能设计出一条满足要求的路径。
题解
-
把圆方树建出来,在树中任意枚举两个圆点作为s和f,然后考虑c有多少种选法。
-
令每个圆点的权值为-1,每个方点的权值为点双大小,那么选法就是两点路径的权值和
-
也就是说我们要求圆方树上n^2条圆点到圆点的路径的权值和,很容易想到计算每个点被算了多少次
代码
1 #include <cstdio> 2 #include <iostream> 3 #define N 400010 4 using namespace std; 5 int n,m,tot,cnt,num,sum,head[N],last[N],stack[N],sz[N],val[N],dfn[N],low[N]; 6 long long ans; 7 struct edge int to,from;e[N],E[N]; 8 void insert(int x,int y) 9 10 e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt; 11 e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt; 12 13 void ins(int x,int y) E[++num].to=y,E[num].from=last[x],last[x]=num; 14 void tarjan(int x) 15 16 dfn[x]=low[x]=++dfn[0],stack[++stack[0]]=x,sz[x]=1,val[x]=-1; 17 for (int i=head[x],l;i;i=e[i].from) 18 if (!dfn[e[i].to]) 19 20 tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]); 21 if (low[e[i].to]>=dfn[x]) 22 23 ins(x,++tot),val[tot]=1,l=0; 24 do l=stack[stack[0]--],ins(tot,l),sz[tot]+=sz[l],++val[tot]; while (l!=e[i].to); 25 sz[x]+=sz[tot]; 26 27 28 else low[x]=min(low[x],dfn[e[i].to]); 29 30 void dfs(int x) 31 32 if (x<=n) ans+=1ll*(sum-1)*val[x]; 33 ans+=1ll*(sum-sz[x])*sz[x]*val[x]; 34 for (int i=last[x];i;i=E[i].from) ans+=1ll*(sum-sz[E[i].to])*sz[E[i].to]*val[x],dfs(E[i].to); 35 36 int main() 37 38 scanf("%d%d",&n,&m),tot=n; 39 for (int x,y;m;m--) scanf("%d%d",&x,&y),insert(x,y); 40 for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i),sum=sz[i],dfs(i); 41 printf("%lld",ans); 42
以上是关于[圆方树] Luogu P4630 Duathlon 铁人两项的主要内容,如果未能解决你的问题,请参考以下文章
P4630 [APIO2018] Duathlon 铁人两项