先让我们探索一下两条非树边以及树边能构成简单环的条件是什么,你会发现将第一条非树边的两个点在树上形成的链记为 \(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的更多相关文章

  1. [USACO19JAN]Exercise Route

    题目 这题的数据有点水,暴力合并\(set\)好像过了 分析一下这个题的性质,发现我们一条非树边就会形成一个环,而我们要求选择两个非树边,就会形成两个环,要求不走重复的点,就是说我们需要走一个大环,且 ...

  2. [USACO18DEC]The Cow Gathering P

    首先可以思考一下每次能删去的点有什么性质. 不难发现,每次能删去的点都是入度恰好为 \(1\) 的那些点(包括 \(a_i \rightarrow b_i\) 的有向边). 换句话说,每次能删去的点既 ...

  3. Application Request Route实现IIS Server Farms集群负载详解

    序言 随着公司业务的发展,后台业务就变的越来越多,然而服务器的故障又像月经一样,时不时的汹涌而至,让我们防不胜防.那么后台的高可用,以及服务器的处理能力就要做一个横向扩展的方案,以使后台业务持续的稳定 ...

  4. .net core 源码解析-mvc route的注册,激活,调用流程(三)

    .net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...

  5. angular路由——ui.route

    angular路由 使用案例 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  6. 如何在ARM中创建Express Route

    很早之前就想试试Azure的express route,但是一直没有找到合适的机会,正好有个客户需要上express route,所以最近先自己研究研究,防止在做poc的时候耗费更多时间,本次场景我们 ...

  7. Python flask @app.route

    转载自 http://python.jobbole.com/80956/ 下面是Flask主页给我们的第一个例子,我们现在就由它入手,深入理解“@app.route()”是如何工作的.         ...

  8. MIT 6.828 JOS学习笔记12 Exercise 1.9

    Lab 1中Exercise 9的解答报告 Exercise 1.9: 判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间 ...

  9. MIT 6.828 JOS学习笔记13 Exercise 1.10

    Lab 1 Exercise 10 为了能够更好的了解在x86上的C程序调用过程的细节,我们首先找到在obj/kern/kern.asm中test_backtrace子程序的地址, 设置断点,并且探讨 ...

随机推荐

  1. Tomcat 组成与工作原理

    开源的 Java Web 应用服务器,实现了 Java EE(Java Platform Enterprise Edition)的部分技术规范,比如 Java Servlet.Java Server ...

  2. 第四个知识点 P类复杂问题

    第四个知识点 P类复杂问题 原文地址:http://bristolcrypto.blogspot.com/2014/10/52-things-number-4-complexity-class-p.h ...

  3. LT7211替代芯片|低BOM成本替代LT7211 EDP转LVDS转换设计芯片CS5211

    LT7211B是一种用于虚拟现实/显示应用的TYPE-C/DP1.2转LVDS转换芯片.LT7211B 对于DP1.2输入,LT7211B可以配置为1.2.4车道,还支持车道交换功能.自适应均衡使其适 ...

  4. Java练习小题_猴子吃桃问题分别用for循环和while循环实现程序。

    要求说明: 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个.以后每天早上都吃了前一天剩下的一半零一个.到第10天早上想再吃时, ...

  5. 编写Java程序,将一个int型数组拼接成字符串

    返回本章节 返回作业目录 需求说明: 将一个int数组中的元素拼接成int元素以逗号分隔字符串. 实现思路: 定义一个数组变量int[] arrs = {12,21,33,9,2}. 定义一个方法ar ...

  6. 分布式抽奖秒杀系统,DDD架构设计和实现分享

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.用大项目,贯穿知识体系 写CRUD.堆API.改屎山⛰,熬多少个996也只是成为重复的螺丝 ...

  7. 微服务探索之路01篇.net6.0项目本地win10系统docker到服务器liunx系统docker的贯通

    本文介绍从创建 net6.0 项目运行在 windows 开发环境的 docker 然后正式部署至 liunx 服务器. 1 windows10 安装 docker 下载docker-desktop ...

  8. Python常用功能函数系列总结(五)

    本节目录 常用函数一:向量距离和相似度计算 常用函数二:pagerank 常用函数三:TF-IDF 常用函数四:关键词提取 常用函数一:向量距离和相似度计算 KL距离.JS距离.余弦距离 # -*- ...

  9. WebLogic任意文件上传漏洞(CVE-2019-2725)

    一,漏洞介绍 1.1漏洞简介 Oracle weblogic反序列化远程命令执行漏洞,是根据weblogic的xmldecoder反序列化漏洞,只是通过构造巧妙的利用链可以对Oracle官方历年来针对 ...

  10. 如何对K8s进行考核?Kuberhealthy来打个样!

    2019年11月,在圣地亚哥KubeCon,我们发布了kuberhealth 2.0.0--将kuberhealthy作为合成监测的Kubernetes operator.这个新功能为开发人员提供了创 ...