洛谷P4630 铁人两项--圆方树
一道很好的圆方树入门题
感谢PinkRabbit巨佬的博客,讲的太好啦
首先是构建圆方树的代码,也比较好想好记
void tarjan(int u) {
dfn[u] = low[u] = ++dfn_clk; //初始化dfn和low数组
stk[++tp] = u; //把u加入栈中
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(!dfn[v]) { //v还未访问
tarjan(v); //先访问
low[u] = min(low[u], low[v]); //然后更新u的信息
if(low[v] == dfn[u]) { //找到一个以u为顶点的点双
++tot; //新建一个方点
for(int x = 0; x != v; --tp) { //把栈中在v及其之前的点都向方点连边并弹出
x = stk[tp];
G[tot].push_back(x);
G[x].push_back(tot);
}
G[tot].push_back(u); //注意不能把u弹出
G[u].push_back(tot); //因为u可能在多个点双中
}
}
else low[u] = min(low[u], dfn[v]);
}
}
注释写的还算详细\(QWQ\)
考虑这一题怎么做
题目大意
给你一张无向图,让你求这样的有序三元组\(<s,c,f>\)的个数,使得存在一条简单路径依次经过\(s,c,f\)
Solution
首先我们把圆方树建出来
考虑如下性质,对于在同一个点双中的两点\(s,t\),还有一个给定的也在这个点双中的点\(c\),一定存在一条简单路径依次经过\(s,c,t\),貌似挺显然的
在这题中,假设钦定了路径的两个端点\(s,t\),由上面的性质,那么能作为中间点的点集就是在圆方树上\(s\)到\(t\)的路径所经过的方点所代表的点双的并集(不包括\(s,t\))。这句话是本题的突破点,虽然有点拗口
然后是一个很套路的处理,把方点的点权设为点双的大小,圆点的点权设为\(-1\),这样的话上面要求的值就转化为\(s\)到\(t\)路径上的点权之和了
直接枚举\(s\)和\(t\)显然不行,考虑枚举每个点对答案的贡献,即
\]
然后用树形\(dp\)就可以\(O(n)\)的统计答案了
另外,注意图不一定联通,所以需要单独统计每个联通块中的答案
#include <bits/stdc++.h>
using namespace std;
#define N 500000
#define M 200000
int n, m, tot;
int head[N+5], eid;
int dfn[N+5], low[N+5], dfn_clk;
int stk[N+5], tp, val[N+M+5], vis[N+M+5], cnt[N+M+5], sz[N+M+5], S;
long long ans;
vector<int> G[N+5];
struct Edge {
int next, to;
}e[2*M+5];
void addEdge(int u, int v) {
e[++eid].next = head[u];
e[eid].to = v;
head[u] = eid;
}
void tarjan(int u) {
dfn[u] = low[u] = ++dfn_clk; //初始化dfn和low数组
stk[++tp] = u; //把u加入栈中
for(int i = head[u]; i; i = e[i].next) {
int v = e[i].to;
if(!dfn[v]) { //v还未访问
tarjan(v); //先访问
low[u] = min(low[u], low[v]); //然后更新u的信息
if(low[v] == dfn[u]) { //找到一个以u为顶点的点双
++tot; //新建一个方点
for(int x = 0; x != v; --tp) { //把栈中在u之前的点都向方点连边并弹出
x = stk[tp];
G[tot].push_back(x);
G[x].push_back(tot);
}
G[tot].push_back(u); //注意不能把u弹出
G[u].push_back(tot); //因为u可能在多个点双中
}
}
else low[u] = min(low[u], dfn[v]);
}
}
void getcnt(int u, int fa) {
if(u <= n) cnt[u] = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == fa) continue;
getcnt(v, u);
cnt[u] += cnt[v];
}
}
void dp(int u, int fa) {
vis[u] = 1;
if(u <= n) sz[u] = 1;
long long sum = 1LL*cnt[u]*(S-cnt[u]);
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(v == fa) continue;
dp(v, u);
sum += 1LL*sz[u]*sz[v];
sz[u] += sz[v];
}
ans += 1LL*val[u]*sum;
}
int main() {
scanf("%d%d", &n, &m);
tot = n;
for(int i = 1, x, y; i <= m; ++i) {
scanf("%d%d", &x, &y);
addEdge(x, y), addEdge(y, x);
}
for(int i = 1; i <= n; ++i)
if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; ++i) val[i] = -1;
for(int i = n+1; i <= tot; ++i) val[i] = G[i].size();
for(int i = 1; i <= tot; ++i)
if(!vis[i]) {
getcnt(i, 0);
S = cnt[i];
dp(i, 0);
}
printf("%lld\n", ans*2);
return 0;
}
洛谷P4630 铁人两项--圆方树的更多相关文章
- [APIO2018] Duathlon 铁人两项 圆方树,DP
[APIO2018] Duathlon 铁人两项 LG传送门 圆方树+简单DP. 不会圆方树的话可以看看我的另一篇文章. 考虑暴力怎么写,枚举两个点,答案加上两个点之间的点的个数. 看到题面中的一句话 ...
- [APIO2018]铁人两项 --- 圆方树
[APIO2018] 铁人两项 题目大意: 给定一张图,问有多少三元组(a,b,c)(a,b,c 互不相等)满足存在一条点不重复的以a为起点,经过b,终点为c的路径 如果你不会圆方树 ------- ...
- [APIO2018]铁人两项 [圆方树模板]
把这个图缩成圆方树,把方点的权值设成-1,圆点的权值设成点双的size,算 经过这个点的路径的数量*这个点的点权 的和即是答案. #include <iostream> #include ...
- [APIO2018]铁人两项——圆方树+树形DP
题目链接: [APIO2018]铁人两项 对于点双连通分量有一个性质:在同一个点双里的三个点$a,b,c$,一定存在一条从$a$到$c$的路径经过$b$且经过的点只被经过一次. 那么我们建出原图的圆方 ...
- 【Luogu4630】【APIO2018】 Duathlon 铁人两项 (圆方树)
Description 给你一张\(~n~\)个点\(~m~\)条边的无向图,求有多少个三元组\(~(x, ~y, ~z)~\)满足存在一条从\(~x~\)到\(~z~\)并且经过\(~y~\)的 ...
- LOJ 2587 「APIO2018」铁人两项——圆方树
题目:https://loj.ac/problem/2587 先写了 47 分暴力. 对于 n<=50 的部分, n3 枚举三个点,把图的圆方树建出来,合法条件是 c 是 s -> f 路 ...
- loj2587 「APIO2018」铁人两项[圆方树+树形DP]
主要卡在一个结论上..关于点双有一个常用结论,也经常作为在圆方树/简单路径上的良好性质,对于任意点双内互不相同的三点$s,c,t$,都存在简单路径$s\to c\to t$,证明不会.可以参见clz博 ...
- [BZOJ5463][APIO2018]铁人两项(圆方树DP)
题意:给出一张图,求满足存在一条从u到v的长度大于3的简单路径的有序点对(u,v)个数. 做了上一题[HDU5739]Fantasia(点双连通分量+DP),这个题就是一个NOIP题了. 一开始考虑了 ...
- 洛谷4630APIO2018铁人两项(圆方树+dp)
QWQ神仙题啊(据说是今年第一次出现圆方树的地方) 首先根据题目,我们就是求对于每一个路径\((s,t)\)他的贡献就是两个点之间的点数,但是图上问题我并没有办法很好的解决... 这时候考虑圆方树,我 ...
随机推荐
- Vmware安装CentOs7+gitlab(二)
在CentOS系统上安装所需的依赖:ssh,防火墙,postfix(用于邮件通知) ,wget,以下这些命令也会打开系统防火墙中的HTTP和SSH端口访问. 1.安装ssh和python sudo y ...
- 编程语言 Node.js中使用到的npm工具
啥是npm? npm就是(node package manager)包结点管理器,它随同Node.js一起安装的,由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了. 同样可以通过输 ...
- web开发布局---传统布局篇
1.传统布局 盒状模型结合 display 属性.float 浮动以及 position 定位属性设计的各式传统布局形式. 2.说再多不如动手实践,下面举三个例子 html 部分代码: <sec ...
- python的学习笔记01_4基础数据类型列表 元组 字典 集合 其他其他(for,enumerate,range)
列表 定义:[]内以逗号分隔,按照索引,存放各种数据类型,每个位置代表一个元素 特性: 1.可存放多个值 2.可修改指定索引位置对应的值,可变 3.按照从左到右的顺序定义列表元素,下标从0开始顺序访问 ...
- AIDL使用以及原理分析
AIDL使用以及IPC原理分析(进程间通信) 概要 为了大家能够更好的理解android的进程间通信原理,以下将会从以下几个方面讲解跨进程通讯信: 1. 必要了解的概念 2. 为什么要使用aidl进程 ...
- 魅族5.0以上设备(亲测有效)激活Xposed框架的流程
对于喜欢研究手机的哥们来说,大多时候会使用到Xposed框架及种种功能强大的模块,对于5.0以下的系统版本,只要手机能获得ROOT权限,安装和激活Xposed框架是比较简单的,但随着系统版本的持续更新 ...
- ASP.NET Zero--基于令牌的认证&SWAGGER UI
基于令牌的认证 任何应用程序都可以将应用程序中的任何功能认证和使用为API.例如,您可以创建一个移动应用程序消耗相同的API.在本节中,我们将演示来自Postman的API (Google Chrom ...
- Anaconda在Windows上安装不上原因
倒腾了一下午安装Anaconda,[所有程序]中只有一个Anaconda的目录,目录中只有一个anaconda prompt,最后发现是因为环境中原先就安装了Java环境,将之前安装的java环境卸载 ...
- R语言学习——图形初阶之散点图
使用R内置的数据框mtcars,绘制车身重量与每加仑汽油行驶的英里数的散点图,要求横轴为车身重量(wt),纵轴为每加仑汽油行驶的英里数(mpg),并添加最优拟合曲线.标题,输出为pdf文件.代码实现如 ...
- 如何改善SSH连接过慢(效率)
+++++++++++++++++++++++++++++++++++++++++问题:通过SSH链接远程Linux主机过慢.重点:学习如何通过调整ssh_config配置文件,提高SSH连接效率.时 ...