Descroption

原题链接给你一棵\(~n~\)个点的树和\(~m~\)条链,求两两相交的链有多少对,两条链相交当且仅当有至少一个公共点。\(~1 \leq n, m \leq 10 ^ 5~\).

Solution

一个很直观的想法是把每一条链路径上的权值\(+1\),然后计算每一条链内多出来的权值为多少,显然这样是错的,因为两条链的交集可能不止有一个点,那么可以把每一条链路径上的点权\(+1\),边权\(-1\),再算多出来多少就好了。然而我不会这个啊。

考虑一个性质:

两条链相交当且仅当一条链的\(LCA\)在另一条链上

至于怎么证明,可以画图推推反例发现找不到,为了方便,设两条链为\(C1,C2\),若\(~LCA_{C1}~\)不在\(~C2~\)内,可以有两种情况:\(~①~\)\(C1~\)和\(C2~\)没有交集.\(~②~\)\(LCA_{C2}~\)在\(~C1~\)上. 这基于树上结点的父亲的唯一性。于是就把每一条链的\(~LCA~\)的权值\(+1\),最后统计每一条链内权值和就好了,注意减去重复的情况。

Code

#include <bits/stdc++.h>
#define For(i, j, k) for (int i = j; i <= k; ++i)
#define Travel(i, u) for (int i = beg[u], v = to[i]; i; v = to[i = nex[i]])
using namespace std; inline int read() {
int x = 0, p = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if(c == '-') p = -1;
for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * p;
} inline void File() {
freopen("P1167.in", "r", stdin);
freopen("P1167.out", "w", stdout);
} typedef long long ll;
const int N = 1e5 + 10;
int e = 1, beg[N], nex[N << 1], to[N << 1], u, v, tmp[N];
int dep[N], dfn[N], top[N], fa[N], son[N], n, m, siz[N]; struct BIT {
int c[N];
inline void update(int x, int v) { for (; x <= n; x += x & -x) c[x] += v; }
inline int query(int x) { int res = 0; for (; x; x -= x & -x) res += c[x]; return res; }
inline int query(int l, int r) { return query(r) - query(l - 1);}
} T; inline void add(int x, int y) {
to[++ e] = y, nex[e] = beg[x], beg[x] = e;
to[++ e] = x, nex[e] = beg[y], beg[y] = e;
} inline void dfs1(int u, int f = 0) {
dep[u] = dep[fa[u] = f] + 1, siz[u] = 1;
Travel(i, u) if (v ^ f) {
dfs1(v, u), siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
} int clk = 0;
inline void dfs2(int u) {
dfn[u] = ++ clk, top[u] = son[fa[u]] == u ? top[fa[u]] : u;
if (son[u]) dfs2(son[u]);
Travel(i, u) if (v ^ fa[u] && v ^ son[u]) dfs2(v);
} inline int lca(int x, int y, int ty) {
int res = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
res += T.query(dfn[top[x]], dfn[x]), x = fa[top[x]];
}
if (dep[x] < dep[y]) swap(x, y);
return ty ? res + T.query(dfn[y], dfn[x]) : y;
} struct Chain { int x, y, lca; } P[N]; int main() {
File(); n = read(), m = read();
For(i, 2, n) u = read(), v = read(), add(u, v);
dfs1(1), dfs2(1); For(i, 1, m) {
P[i].x = read(), P[i].y = read();
++ tmp[P[i].lca = lca(P[i].x, P[i].y, 0)];
T.update(dfn[P[i].lca], 1);
} ll ans = 0;
For(i, 1, m) ans += lca(P[i].x, P[i].y, 1) - 1;
For(i, 1, n) ans -= 1ll * tmp[i] * (tmp[i] - 1) >> 1ll; cout << ans << endl;
return 0;
}

【hihocoder1167】高等理论计算机科学 (重链剖分 +树状数组)的更多相关文章

  1. Aragorn's Story 树链剖分+线段树 && 树链剖分+树状数组

    Aragorn's Story 来源:http://www.fjutacm.com/Problem.jsp?pid=2710来源:http://acm.hdu.edu.cn/showproblem.p ...

  2. 4.12 省选模拟赛 LCA on tree 树链剖分 树状数组 分析答案变化量

    LINK:duoxiao OJ LCA on Tree 题目: 一道树链剖分+树状数组的神题. (直接nQ的暴力有50. 其实对于树随机的时候不难想到一个算法 对于x的修改 暴力修改到根. 对于儿子的 ...

  3. hdu 3966 Aragorn's Story(树链剖分+树状数组/线段树)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意: 给出一棵树,并给定各个点权的值,然后有3种操作: I C1 C2 K: 把C1与C2的路 ...

  4. HDU 3966 /// 树链剖分+树状数组

    题意: http://acm.hdu.edu.cn/showproblem.php?pid=3966 给一棵树,并给定各个点权的值,然后有3种操作: I x y z : 把x到y的路径上的所有点权值加 ...

  5. HDU 3966 Aragorn's Story 树链剖分+树状数组 或 树链剖分+线段树

    HDU 3966 Aragorn's Story 先把树剖成链,然后用树状数组维护: 讲真,研究了好久,还是没明白 树状数组这样实现"区间更新+单点查询"的原理... 神奇... ...

  6. bzoj1146整体二分+树链剖分+树状数组

    其实也没啥好说的 用树状数组可以O(logn)的查询 套一层整体二分就可以做到O(nlngn) 最后用树链剖分让序列上树 #include<cstdio> #include<cstr ...

  7. HDU 5044 (树链剖分+树状数组+点/边改查)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变 ...

  8. BZOJ 2758 Blinker的噩梦(扫描线+熟练剖分+树状数组)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2758 题意:平面上有n个多边形(凸包和圆).任意两个多边形AB只有两种关系:(1) ...

  9. hdu 3966 Aragorn&#39;s Story(树链剖分+树状数组)

    pid=3966" target="_blank" style="">题目链接:hdu 3966 Aragorn's Story 题目大意:给定 ...

随机推荐

  1. [Offer收割]编程练习赛97

    链接 [https://hihocoder.com/contest/offers97/problems] 题意 题目1 : 放置矩形 时间限制:10000ms 单点时限:1000ms 内存限制:256 ...

  2. D1. Great Vova Wall (Version 1)

    链接 [https://codeforces.com/contest/1092/problem/D1] 题意 给你n个位置墙的高度,现在你有2×1 砖块,你可以竖直或者水平放置 问你是否可以使得所有位 ...

  3. FPGA中边沿触发和电平触发

    边沿触发和电平触发基本就是触发器和锁存器的区别: 触发器是边沿触发,只有当时钟上升(或下降)的一瞬间,触发器会读取并锁存输入信号.输出信号仅在时钟信号上升(或下降)的一瞬间会发生变化.   锁存器是电 ...

  4. 替换iframe的内容

    一般就是点个按钮然后在不跳转页面的情况下显示另外一个页面的内容,显示的速度比较快,ifream还算是常用的吧 用的时候实现方式有以下几种, 1.替换src路径(觉得麻烦,没接触过,就不这样干了) 2. ...

  5. jmeter接口测试------基础笔记

    1.postman发送json格式的post请求,直接放链接 row里面body放请求参数,得到请求结果 2.jmeter请求json时需要注意在请求前创建http信息头管理器,然后信息头添加一条名称 ...

  6. Git-用 cherry-pick 挑好看的小樱桃

         版权声明:本文为博主原创文章,转载请在文章明显位置标明文章原属哦. https://blog.csdn.net/qq_32452623/article/details/79449534 ti ...

  7. linux安装httpd,做文件服务器

    在一个团队或者公司层面上,做一个本地的文件服务器,将网上的资源下载到本地,是有必要的.这将节省其他人的很多下载时间. >>提君博客原创  http://www.cnblogs.com/ti ...

  8. 为什么js中要用void 0 代替undefined

    这个是Backbone.js中的一句源码 if (callback !== void 0 && 'context' in opts && opts.context == ...

  9. python爬虫之git的团队协作

    一.Git实践: commit,push,pull,status,add基本是最常用的几个命令. 1.首先我在github上创建了一个项目,然后我在本地的文件建立了一个普通的目录(git_data). ...

  10. 移动端Web界面滚动touch事件

    解决办法一: elem.addEventListener( 'touchstart', fn, { passive: false } ); 解决办法二: * { touch-action: pan-y ...