原题链接戳这儿

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. 《Tsinghua oc mooc》第5~7讲 物理内存管理

    资源 OS2018Spring课程资料首页 uCore OS在线实验指导书 ucore实验基准源代码 MOOC OS习题集 OS课堂练习 Piazza问答平台 暂时无法注册 疑问 段式内存管理中,逻辑 ...

  2. linux 系统自签免费ssl证书和nginx配置

    首先执行如下命令生成一个key openssl genrsa -des3 -out ssl.key 1024 然后他会要求你输入这个key文件的密码.不推荐输入.因为以后要给nginx使用.每次rel ...

  3. kafka producer serializer序列化(六)

    生产者需要将要发送的数据转换成字节数组才能通过网络发送给kafka,对于一些简单的数据,kafka自带了一些序列化工具, 如:StringSerializer Double Long Integer ...

  4. Scrapy setup.py 各参数详解

    实际上Scrapyd的打包工具用到了setuptools,而打包参数主要是在setuptools里面的setup函数中设置. ************************************* ...

  5. 【AtCoder】ARC068

    ARC 068 C - X: Yet Another Die Game 显然最多的就是一次6一次5 最后剩下的可能需要多用一次6或者6和5都用上 #include <bits/stdc++.h& ...

  6. analysis_tools

  7. 17.tmux相关

    Linux终端复用神器-Tmux使用梳理 Tmux是一个优秀的终端复用软件,类似GNU Screen,但来自于OpenBSD,采用BSD授权.使用它最直观的好处就是,通过一个终端登录远程主机并运行tm ...

  8. [Lua]LuaAPI整理

    ref :https://blog.csdn.net/ouyangshima/article/details/43339571   LUA和C/C++的沟通桥梁——栈 Lua生来就是为了和C交互的,因 ...

  9. 解决python在cmd运行时导入包失败,出现错误信息 "ModuleNotFoundError: No module named ***"

    1.下图为我的自动化测试工程结构图 我通过运行run.bat批处理文件,调用cmd控制台运行start_run.py来开始我的自动化测试,但是出现如下错误: 大家可能知道我们的工程在IDE(Pycha ...

  10. 怎样安装并编译TypeScript?

    1. 使用: npm -v 查看是否安装了 npm ,  如果没有安装, 请前往 Nodejs 官网 下载安装, 下图表示已经安装 npm , 版本为: 6.9.0 . PS C:\Users\Adm ...