原题链接戳这儿

SOLUTION

考虑一种非常\(naive\)的统计方法,就是对于每一个点\(u\),我们维护它能到达的点集\(S_u\),最后答案就是\(\frac{\sum\limits_{i=1}^{n}|S_i|}{2}\)

也就是说我们可以先树剖一下,对于每一个点都开一棵线段树,每次修改\(O(nlogn)\)地更新一下路径上的线段树,最后查询一下就行了

但是这样的复杂度是\(O(n^2log^2n)\)的,显然会炸。注意到每次是对一条链上的所有点操作,所以我们可以查分。又因为差分之后要把子树的贡献传上去,再上个线段树合并就行了,复杂度降为\(O(nlog^2n)\)

代码也比较好写,细节不多

#include <bits/stdc++.h>

using namespace std;

#define N 100000
#define ll long long
#define mp make_pair
#define pii pair<int, int>
#define pb push_back
#define mid ((l + r) >> 1) int n, m;
vector<int> G[N + 5];
int sz[N + 5], fa[N + 5], d[N + 5], hson[N + 5], top[N + 5], dfn[N + 5], dfn_clk, id[N + 5];
int nid, root[N + 5], sumv[N << 7], ch[2][N << 7], addv[N << 7];
vector<pii> cf[N + 5], segs[N + 5];
ll ans; void dfs1(int u, int pa) {
sz[u] = 1;
fa[u] = pa;
for (int i = 0, v; i < G[u].size(); ++i) {
v = G[u][i];
if (v == pa) continue;
d[v] = d[u] + 1;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[hson[u]]) hson[u] = v;
}
} void dfs2(int u, int tp) {
dfn[u] = ++dfn_clk;
id[dfn_clk] = u;
top[u] = tp;
if (hson[u]) dfs2(hson[u], tp);
for (int i = 0, v; i < G[u].size(); ++i) {
v = G[u][i];
if (v == fa[u] || v == hson[u]) continue;
dfs2(v, v);
}
} int lca(int x, int y) {
while (top[x] != top[y]) d[top[x]] > d[top[y]] ? x = fa[top[x]] : y = fa[top[y]];
return d[x] < d[y] ? x : y;
} void addModify(int x, int s, int t) {
int z = lca(s, t);
cf[s].pb(mp(x, 1)), cf[t].pb(mp(x, 1));
cf[z].pb(mp(x, -1)), cf[fa[z]].pb(mp(x, -1));
while (top[s] != top[z]) segs[x].pb(mp(dfn[top[s]], dfn[s])), s = fa[top[s]];
segs[x].pb(mp(dfn[z], dfn[s]));
while (top[t] != top[z]) segs[x].pb(mp(dfn[top[t]], dfn[t])), t = fa[top[t]];
if (dfn[z] + 1 <= dfn[t]) segs[x].pb(mp(dfn[z] + 1, dfn[t]));
} void pushup(int o, int l, int r) {
if (addv[o]) sumv[o] = r - l + 1;
else sumv[o] = sumv[ch[0][o]] + sumv[ch[1][o]];
} void add(int &o, int l, int r, int L, int R, int k) {
if (!o) o = ++nid;
if (L <= l && r <= R) {
addv[o] += k;
pushup(o, l, r);
return ;
}
if (L <= mid) add(ch[0][o], l, mid, L, R, k);
if (R > mid) add(ch[1][o], mid + 1, r, L, R, k);
pushup(o, l, r);
} void merge(int &o, int u, int l, int r) {
if (!o || !u) {
if (!o) o = u;
return ;
}
addv[o] += addv[u];
if (l < r) {
merge(ch[0][o], ch[0][u], l, mid);
merge(ch[1][o], ch[1][u], mid + 1, r);
}
pushup(o, l, r);
} void dfs(int u) {
for (int i = 0, v; i < G[u].size(); ++i) {
v = G[u][i];
if (v == fa[u]) continue;
dfs(v);
merge(root[u], root[v], 1, n);
}
for (int i = 0; i < cf[u].size(); ++i)
for (int j = 0; j < segs[cf[u][i].first].size(); ++j)
add(root[u], 1, n, segs[cf[u][i].first][j].first, segs[cf[u][i].first][j].second, cf[u][i].second);
ans += max(0, sumv[root[u]] - 1); // 注意这里要与0取max
} int main() {
scanf("%d%d", &n, &m);
for (int i = 1, x, y; i < n; ++i) {
scanf("%d%d", &x, &y);
G[x].pb(y), G[y].pb(x);
}
dfs1(1, 0), dfs2(1, 1);
for (int i = 1, s, t; i <= m; ++i) {
scanf("%d%d", &s, &t);
addModify(i, s, t);
}
dfs(1);
ans /= 2;
printf("%lld\n", ans);
return 0;
}

[ZJOI2019]语言——树剖+树上差分+线段树合并的更多相关文章

  1. [Luogu5327][ZJOI2019]语言(树上差分+线段树合并)

    首先可以想到对每个点统计出所有经过它的链的并所包含的点数,然后可以直接得到答案.根据实现不同有下面几种方法.三个log:假如对每个点都存下经过它的链并S[x],那么每新加一条路径进来的时候,相当于在路 ...

  2. [BZOJ3307] 雨天的尾巴(树上差分+线段树合并)

    [BZOJ3307] 雨天的尾巴(树上差分+线段树合并) 题面 给出一棵N个点的树,M次操作在链上加上某一种类别的物品,完成所有操作后,要求询问每个点上最多物品的类型. N, M≤100000 分析 ...

  3. [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮. 然后深绘里想知道,当所有的救济粮 ...

  4. Luogu5327 ZJOI2019语言(树上差分+线段树合并)

    暴力树剖做法显然,即使做到两个log也不那么优美. 考虑避免树剖做到一个log.那么容易想到树上差分,也即要对每个点统计所有经过他的路径产生的总贡献(显然就是所有这些路径端点所构成的斯坦纳树大小),并 ...

  5. bzoj 3307: 雨天的尾巴【树剖lca+树上差分+线段树合并】

    这居然是我第一次写线段树合并--所以我居然在合并的时候加点结果WAWAWAMLEMLEMLE--!ro的时候居然直接指到la就行-- 树上差分,每个点建一棵动态开点线段树,然后统计答案的时候合并即可 ...

  6. BZOJ4999:This Problem Is Too Simple!(DFS序&树上差分&线段树动态开点:区间修改单点查询)

    Description 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x&l ...

  7. 2018.08.28 洛谷P4556 [Vani有约会]雨天的尾巴(树上差分+线段树合并)

    传送门 要求维护每个点上出现次数最多的颜色. 对于每次修改,我们用树上差分的思想,然后线段树合并统计答案就行了. 注意颜色很大需要离散化. 代码: #include<bits/stdc++.h& ...

  8. BZOJ 3307 雨天的尾巴 (树上差分+线段树合并)

    题目大意:给你一棵树,树上一共n个节点,共m次操作,每次操作给一条链上的所有节点分配一个权值,求所有节点被分配到所有的权值里,出现次数最多的权值是多少,如果出现次数相同就输出最小的. (我辣鸡bzoj ...

  9. P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并 (树上差分+线段树合并)

    显然的树上差分问题,最后要我们求每个点数量最多的物品,考虑对每个点建议线段树,查询子树时将线段树合并可以得到答案. 用动态开点的方式建立线段树,注意离散化. 1 #include<bits/st ...

随机推荐

  1. Bootstrap 表单示例

    1.打开https://getbootstrap.com/docs/4.3/examples/checkout/复制表单源码 2.清空main标签内容 3.粘贴表单源码 4.示例图

  2. 33.TCP协议概念/scapy模块doos攻击

    TCP协议概念/scapy模块: 1,TCP/IP四层协议: 2,TCP数据包的构成: TCP FLAGS:TCP数据包标志位 U:URG,紧急比特 A:ACK,确认比特 P:PSH,推送比特 S:S ...

  3. Linux 如何找到100M以上的大文件

    find / -type f -size +100000k |xargs ls -lh|awk '{print $9 ":" $5}'

  4. new pdo 连接很慢的原因和解决办法

    1.使用IP而不是域名,使用域名会让PDO在连接之前进行一次不必要的dns lookup,当DNS缓存过于巨大的时候,这个问题可能会更严重.(即使是运行在本机,使用 127.0.0.1 而不是 loc ...

  5. 关于日志slf4j+logback&logback.xml配置

    1.maven依赖 <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <!-- <dependen ...

  6. 2.3负载均衡:Ribbon

    基于上一篇文章的工程,启动eureka-server 工程:启动service-hi工程,它的端口为8765:将service-hi的配置文件的端口改为8763,并启动,这时你会发现:service- ...

  7. django类视图的使用

    1 类视图引入 以函数的方式定义的视图称为函数视图,函数视图便于理解. 但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳 ...

  8. Python 运算符与数据类型

    Python 的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承.Py ...

  9. JSON运用在文件

    #include <iostream>#include <fstream>#define JSON_IS_AMALGAMATION#include "json/jso ...

  10. 设计模式风格<一>;回调风格

    主程序,是一个人,有一个类是同事: static void Main(string[] args) { Console.WriteLine("Hello Go to Lunch?" ...