[USACO19JAN]Exercise Route P
先让我们探索一下两条非树边以及树边能构成简单环的条件是什么,你会发现将第一条非树边的两个点在树上形成的链记为 \(W_1\),另一条即为 \(W_2\),那么当且仅当 \(W_1, W_2\) 有交时才能满足条件。因为当 \(W_1, W_2\) 没交时,那么中间一定会经过一些树边不被这两条链覆盖,但因为点只能走一次因此我们就不能回来了,所以这种情况下是不行的;而当 \(W_1, W_2\) 有交时很容易就可以构造出一条合法的简单环。
于是现在问题转化成统计有多少对链 \(W_1, W_2\) 满足其有交。这种链交问题我们一般拆成左右两条直上直下的链,这样可以减少讨论。令 \(W_1, W_2\) 中链顶更高的为 \(W_1\),不难发现两条链有交当且仅当 \(W_1\) 会穿过 \(W_2\) 的链顶与其链上儿子中间组成的这条边,因此对于每条链 \(W_i\) 我们在其链顶与其在链上的儿子组成的边上权值 \(+1\),那么和每条链交的链的数量就是其在链上的边上的权值之和,这个我们可以直接倍增计算得出,这部分复杂度 \(O(n \log n)\)。于此同时你会发现,如果两条链链顶不同,这样只会计算一次链交,如果两条链顶相同,这样会计算两次,因此我们还需要将这部分多余的减去。
不难发现我们将所有链挂在链顶上,那么链顶相同且有交的链当且仅当是都经过了这个链顶的一个儿子的这样一对链。于是我们把经过每个儿子的链的数量统计出来,令第 \(i\) 个儿子的数量为 \(c_i\),则多算的链数应该是:\(\dbinom{c_i}{2}\),减去即可,这部分复杂度 \(O(n)\)。
但是回过头来会发现一个问题,有没有可能两条链在左边边交一次右边边也交一次呢,这样就记重了。事实上是有的,当且仅当两条链的 \(LCA\) 相同并且两条链都相同地经过 \(LCA\) 的两个儿子。这部分记重我们只需要将所有链一样地挂在 \(LCA\) 上每次用 \(map\) 统计经过两个儿子的链的数量,令其中一种的数量为 \(x\),则多算的部分应该是:\(\dbinom{x}{2}\),减去即可。这部分复杂度 \(O(n \log n)\)。
一些坑点
- 就算原来的链也是直上直下的也会计算重复,也需要减去算重的部分;于此同时需要注意第二次去重时不要统计经过其自身的和一个儿子的重复部分。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i, l, r) for(int i = l; i <= r; ++i)
#define dep(i, l, r) for(int i = r; i >= l; --i)
#define Next(i, u) for(int i = h[u]; i; i = e[i].next)
const int N = 200000 + 5;
const int K = 20 + 5;
struct edge{
int v, next;
}e[N << 1];
struct node{
int u, v;
}a[N << 1];
long long ans;
int n, m, u, v, tot, cnt, Lca, h[N], dep[N], tmp[N], f[N][K], dp[N][K];
vector <node> G[N];
map <int, int> M[N];
int read(){
char c; int x = 0, f = 1;
c = getchar();
while(c > '9' || c < '0'){ if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
void add(int u, int v){
e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
e[++tot].v = u, e[tot].next = h[v], h[v] = tot;
}
void dfs(int u, int fa){
f[u][0] = fa, dep[u] = dep[fa] + 1;
Next(i, u) if(e[i].v != fa) dfs(e[i].v, u);
}
int LCA(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
dep(i, 0, 20) if(dep[f[x][i]] >= dep[y]) x = f[x][i];
if(x == y) return x;
dep(i, 0, 20) if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
int find(int x, int y){
dep(i, 0, 20) if(dep[f[x][i]] > dep[y]) x = f[x][i];
return x;
}
int calc(int x, int y){
int ans = 0;
dep(i, 0, 20) if(dep[f[x][i]] >= dep[y]) ans += dp[x][i], x = f[x][i];
return ans;
}
signed main(){
n = read(), m = read();
rep(i, 1, n - 1) u = read(), v = read(), add(u, v);
dfs(1, 0);
rep(j, 1, 20) rep(i, 1, n) f[i][j] = f[f[i][j - 1]][j - 1];
rep(i, n, m){
u = read(), v = read(), Lca = LCA(u, v); if(dep[u] < dep[v]) swap(u, v);
G[Lca].push_back((node){find(u, Lca), find(v, Lca)});
if(v != Lca) a[++cnt] = (node){u, Lca}, a[++cnt] = (node){v, Lca};
else a[++cnt] = (node){u, v};
}
rep(i, 1, n){
if(!G[i].size()) continue;
for(int j = 0; j < G[i].size(); ++j){
if(G[i][j].u > G[i][j].v) swap(G[i][j].u, G[i][j].v);
if(G[i][j].u != i && G[i][j].v != i) ans -= M[G[i][j].u][G[i][j].v];
++tmp[G[i][j].u], ++tmp[G[i][j].v], ++M[G[i][j].u][G[i][j].v];
}
Next(j, i) ans -= 1ll * tmp[e[j].v] * (tmp[e[j].v] - 1) / 2;
for(int j = 0; j < G[i].size(); ++j) --tmp[G[i][j].u], --tmp[G[i][j].v], --M[G[i][j].u][G[i][j].v];
}
rep(i, 1, cnt) ++dp[find(a[i].u, a[i].v)][0];
rep(j, 1, 20) rep(i, 1, n) dp[i][j] = dp[i][j - 1] + dp[f[i][j - 1]][j - 1];
rep(i, 1, cnt) ans += calc(a[i].u, a[i].v) - 1;
printf("%lld", ans);
return 0;
}
值得一提的时其实在第一次计算时不需要倍增去统计,我们直接利用差分的技巧统计链底到根的和减去链顶到根的和即可。
[USACO19JAN]Exercise Route P的更多相关文章
- [USACO19JAN]Exercise Route
题目 这题的数据有点水,暴力合并\(set\)好像过了 分析一下这个题的性质,发现我们一条非树边就会形成一个环,而我们要求选择两个非树边,就会形成两个环,要求不走重复的点,就是说我们需要走一个大环,且 ...
- [USACO18DEC]The Cow Gathering P
首先可以思考一下每次能删去的点有什么性质. 不难发现,每次能删去的点都是入度恰好为 \(1\) 的那些点(包括 \(a_i \rightarrow b_i\) 的有向边). 换句话说,每次能删去的点既 ...
- Application Request Route实现IIS Server Farms集群负载详解
序言 随着公司业务的发展,后台业务就变的越来越多,然而服务器的故障又像月经一样,时不时的汹涌而至,让我们防不胜防.那么后台的高可用,以及服务器的处理能力就要做一个横向扩展的方案,以使后台业务持续的稳定 ...
- .net core 源码解析-mvc route的注册,激活,调用流程(三)
.net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...
- angular路由——ui.route
angular路由 使用案例 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...
- 如何在ARM中创建Express Route
很早之前就想试试Azure的express route,但是一直没有找到合适的机会,正好有个客户需要上express route,所以最近先自己研究研究,防止在做poc的时候耗费更多时间,本次场景我们 ...
- Python flask @app.route
转载自 http://python.jobbole.com/80956/ 下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的. ...
- MIT 6.828 JOS学习笔记12 Exercise 1.9
Lab 1中Exercise 9的解答报告 Exercise 1.9: 判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间 ...
- MIT 6.828 JOS学习笔记13 Exercise 1.10
Lab 1 Exercise 10 为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址, 设置断点,并且探讨 ...
随机推荐
- Momentum and NAG
目录 Momentum Nesterov accelerated gradient NESTEROV 的另外一个方法? Momentum Momentum的迭代公式为: \[v_t = \gamma ...
- RTD2172替代方案|TYPEC转HDMI4K@60HZ拓展坞|CS5265替代RTD2172
瑞昱RTD2172是TYPEC转HDMI4K60HZ音视频数据转换器芯片.CS5265可以替代兼容RTD2172,除了实现同等的转换功能外且整体方案成本和性价比方面比RTD2172要高,且外围器件较少 ...
- JDK中的BitMap实现之BitSet源码分析
前提 本文主要内容是分析JDK中的BitMap实现之java.util.BitSet的源码实现,基于JDK11编写,其他版本的JDK不一定合适. 文中的图比特低位实际应该是在右边,但是为了提高阅读体验 ...
- 把rocksdb配置成leveldb
将rocksdb配置成leveldb 1.配置方法 配置方式有三种: 第一种适合进行性能测试对比:是以参数形式在运行db_bench或ycsb-c的时候以参数形式将rocksdb将其配置成leveld ...
- 4 - 基于ELK的ElasticSearch 7.8.x技术整理 - 高级篇( 续 ) - 更新完毕
0.前言 这里面一些理论和前面的知识点挂钩的,所以:建议看一下另外3篇知识内容 基础篇:https://www.cnblogs.com/xiegongzi/p/15684307.html java操作 ...
- c# - 按引用内存地址传参 和 按输出传参 的具体使用
1.前言 传递参数,不需要返回值,对懒人很舒服哟,缺点是不好定位数据 2.操作 using System; namespace ConsoleApp1.letVlaueGo { public clas ...
- StringBuffer和String的区别
面试题:String为什么不可变 StringBuffer和StringBuilder的区别 String 和StringBuffer的区别: (一):String 类中的byte数组使用final修 ...
- rootckeck
rootcheck rootcheck1.问题描述2.analysis3.solution4.总结 1.问题描述 经常会有听说root手机,其实质就是让使用手机的人获得手机系统的最大权限.因为andr ...
- Tomcat部署启动时发生错误
Tomcat启动后项目地址显示404:源服务器未能找到目标资源的表示或者是不愿公开一个已经存在的资源表示. 严重: ContainerBase.addChild: start: org.apache. ...
- 【代码分享】用redis+lua实现多个集合取交集并过滤,类似于: select key from set2 where key in (select key from set1) and value>=xxx
redis中的zset结构可以看成一个个包含数值的集合,或者认为是一个关系数据库中用列存储方式存储的一列. 需求 假设我有这样一个数据筛选需求,用SQL表示为: select key from set ...