原题链接戳这儿

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. eduSOHO 首页模板 全部课程模块代码

    首页模板文件 设置在后台主题-管理-选中网校课程 然后前台调用代码 {% if code != 'course-grid-with-condition-index' %}        {% cach ...

  2. img的src为空的时候,去除灰色的边框

  3. 内存泄漏之malloc替换方法

    //内存泄漏之malloc替换方法 //内存泄漏之malloc替换方法#include "stdio.h"#include "stdlib.h" /*文件路径名 ...

  4. Linux中实用的命令

    1. 查看linux机器是32位还是64位的方法: 1.file  /sbin/init 或者file  /bin/ls           (注意命令中的空格) /sbin/init: ELF64- ...

  5. java源码--HashMap

    一.HashMap简介 1.1.HashMap概述 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素 ...

  6. javascript 仿jQuery的无new构造函数

    /* 匿名函数 传入 window 值全局变量成为局部变量 */ (function(window,undefined) { /* 申明一个名为jQuery 的函数*/ function jQuery ...

  7. 怎样设置HTTP请求头Header

    使用: xhr.setRequestHeader(); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequest ...

  8. vue学习【一、开发环境搭建】

    一.安装node.js https://nodejs.org/en/ 建议安装LTS版本 安装完毕之后cmd命令查看node版本,如果不识别,记住设置环境变量 显示上面信息则安装成功 二.设置node ...

  9. 开始学Python 啦 ,持续不断总结中。。(转)快捷键的使用

    最重要的快捷键1. ctrl+shift+A:万能命令行2. shift两次:查看资源文件新建工程第一步操作1. module设置把空包分层去掉,compact empty middle packag ...

  10. pthread 编程基础

    Linux系统下的多线程遵循POSIX线程接口,称为pthread.编写Linux下的多线程程序,需要使用头文件pthread.h,连接时需要使用库libpthread.a.与vxworks上任务的概 ...