[圆方树] Luogu P4630 Duathlon 铁人两项

题目描述

比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成。

最近,比特镇获得了一场铁人两项锦标赛的主办权。这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程。

比赛的路线要按照如下方法规划:

  1. 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点、切换点(运动员在长跑到达这个点后,骑自行车前往终点)、终点。
  2. 选择一条从 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  } 

原文地址:https://www.cnblogs.com/Comfortable/p/11281783.html

时间: 2024-10-09 18:21:34

[圆方树] Luogu P4630 Duathlon 铁人两项的相关文章

luogu 4630 [APIO2018] Duathlon 铁人两项

题目大意: 无向图上找三个点 a b c使存在一条从a到b经过c的路径 求取这三个点的方案数 思路: 建立圆方树 这个圆方树保证没有两个圆点相连或两个方点相连 对于每个节点x 设该节点为路径的中间节点 则a c要么同在一个子树内 要么一个在子树内另一个在子树外 最后对答案<<1 对于每个方点设val[x] 为该点所连圆点的个数 每个方点按照两种情况算出答案之后 用圆点减去算重的部分 1 #include<iostream> 2 #include<cstdio> 3 #i

【P4320】 道路相遇 (圆方树+LCA)

题目链接 题意:给一张无向图和\(M\)个询问,问\(u,v\)之间的路径的必经之点的个数. 对图建出圆方树,然后必经之点就是两点路径经过的原点个数,用\((dep[u]+dep[v]-dep[LCA]*2)/2+1\)即可算出. 什么你不知道圆方树(说的跟我知道一样) \(APIO2018\)出来的黑科技,详见\(APIO2018\)铁人两项. 就是对每个点双新建一个点,然后让点双里所有点都对这个点连边. 看图. #include <cstdio> const int MAXN = 5000

圆方树学习

圆方树是一种数据结构. 这个东西原始的出处应该是paper <Maintaining bridge-connected and biconnected components on-line> tarjan和另外一个人写的...当时叫forest data structure 然后这个东西似乎已经流行很久了?http://blog.csdn.net/PoPoQQQ/article/details/49513819 cjk大爷最近发了一篇博客写这个:http://immortalco.blog.u

Tourists——圆方树

CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么,从任意一个割点进入这个DCC,必然可以绕一圈再从另一个割点出去. 所以,路径上的最小值,就是圆方树路径上的最小值.方点的最小值就是在这个DCC中走一走得到的. 树链剖分+线段树维护路径 用堆维护方点四周的圆点的最小值.然后更新. 一个问题是: 更新一个割点圆点,会影响到四周所有的方点.暴力更新,菊花

[SDOI2018]战略游戏 圆方树,树链剖分

[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中的圆点个数减去\(S\).问题变成了怎样求这样的连通块中的圆点个数,直接给结论吧:先搞出树的dfs序,把询问的点按dfs序从小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)个点之间的圆点个数,但是不算lca,再加上第\(1\)个和第\(S\)个点之间的圆点个数,然后除以二就得到了这个

圆方树总结

圆方树:一种将由图转化而成的树,从而大大了增加题目的可解性,且大多广泛用于仙人掌图中. 针对仙人掌图上的圆方树:仙人掌是指一条边至多只被一个环包含的无向图. 树上的点:圆方树上分为两类点,一类是圆点,一类是方点.圆点即原图中所有的点,方点即为了去环而新添加进去的,满足一定性质的点. 构造思路:圆圆边直接加入,对于仙人掌中的任意一个环,每个环上的点在圆方树上对应的圆点向这个环对应的方点连边,方点为一个新建节点. 环的根:指定一个圆点为圆方树的根,把方点的父亲叫做这个方点对应的环的根. 圆方边边权:

圆方树小结

圆方树 jzoj 1914. [2011集训队出题]最短路 这是道圆方树+倍增LCA裸题. 圆方树,顾名思义,就是圆点和方点所组成的树. 而方点就是一个圆的根,一般都是\(dfs\)时第一个到这个圆的那个位置,然后另附一个点当做方点.然后圆所组成的点都连向方点. 而对于这种圆方边的边权,则为它到根的最近值. 从而将一个仙人掌转成了一棵树. 然后对于这棵树,我们就可以用倍增来求出两两点之间的最短路了. 注意的是,对于最后走到的位置,我们要看看它是从上面绕近还是直接从下面走更优!!! 就这样子了.

【BZOJ】2125: 最短路 圆方树(静态仙人掌)

[题意]给定带边权仙人掌图,Q次询问两点间最短距离.n,m,Q<=10000 [算法]圆方树处理仙人掌问题 [题解]树上的两点间最短路问题,常用倍增求LCA解决,考虑扩展到仙人掌图. 先对仙人掌图建圆方树,圆圆边和原图边权一致.对于每个方点代表的环,记深度最小的点为x,则圆方边的边权是圆点到x的最短距离. 若lca(u,v)为圆点,则两点间最短路转化为圆方树上dis[u]+dis[v]-2*dis[lca].(向上延伸的路径,经过环则必然经过每个方点的x,计算无误) 若lca(u,v)为方点,则

CF487E Tourists 【圆方树 + 树剖 + 堆】

题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用了一对一的特性优化了复杂度 如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可 复杂度\(O(qlog^2n)\) #include<algorithm> #include<iostream> #include<cstring> #include<