题意

给你一颗有 \(n\) 个点并且以 \(1\) 为根的树。共有 \(q\) 次询问,每次询问两个参数 \(p, k\) 。询问有多少对点 \((p, a, b)\) 满足 \(p,a,b\) 为三个不同的点,\(p, a\) 都为 \(b\) 的祖先,且 \(p\) 到 \(a\) 的距离不能超过 \(k\) 。

\(n\le 300000 , q\le 300000\) 不要求强制在线。

题解

令 \(dep[u]\) 为点 \(u\) 的深度,\(sz[u]\) 为 \(u\) 的子树大小(除去 \(u\) 本身)

首先我们考虑两种情况:

  1. \(a\) 为 \(p\) 的祖先,那么这部分贡献很好计算,就是 \(\min\{dep[p] - 1,k\} \times sz[u]\) 。
  2. \(a\) 在 \(p\) 的子树内,那么这部分贡献就是 \(\displaystyle \sum_{dis(p,a) \le k} sz[a]\) 。

我们现在只要考虑第二部分贡献怎么求。

不难发现,这些点的深度就是 \([dep[p], dep[p]+k]\) 这个范围内的。

那么我们可以对于每个点用个 主席树 来存储这些信息,可以在线回答询问。

那么离线的话,可以考虑用 线段树合并 维护它每个子树的信息。

具体来说,这些都是对于每个 \(dep\) 维护它的 \(sz\) 的和,然后查区间和就行了。

然而这些时空复杂度都是 \(O(n \log n)\) ,其实还有更好的做法。

为什么我发现了呢qwq?

因为 fatesky 做这道题线段树合并做法的时候,Wearry 说可以 长链剖分 那就是 \(O(n)\) 的啦。

我们令 \(\displaystyle maxdep[u]=\max_{v \in child[u]} \{dep[v\}\) 也就是它子树中的最大深度。

具体来说,长链剖分就是把每个点儿子中 \(maxdep\) 最大的那个当做重儿子。重儿子与父亲连的边叫做重边。一连串重边不间断连到一起就叫做重链。

然后我们就有一条性质。

性质1 : 重链长度之和是 \(O(n)\) 的。

这个很显然啦,因为总共只有 \(O(n)\) 级别的边。

有了这个我们就可以解决一系列 关于深度的动态规划 问题了,对于这列问题常常都可以做到 \(O(n)\) 的复杂度。

具体操作就是,每次暴力继承重儿子的 \(dp\) 状态,然后轻儿子暴力合并上去。

不难发现这个复杂度是 \(O(\sum\) 重链长 \()\) \(= O(n)\) 的。

继承的时候常常需要移位,并且把当前节点贡献算入,并且这个 \(dp\) 需要动态空间才能实现。

对于这道题我们考虑维护一个后缀和,也就是对于 \(u\) 子树中的 \(v\) ,\(dep[v] \ge k\) 的所有 \(sz[v]\) 的和。

不难发现后缀和是很好合并的,这个的复杂度只需要 \(O(\min maxdep[v])\) 。

每次添加一个点 \(sz[u]\) 对于 \(dep[u]\) 的贡献只会对一个点的贡献产生影响,这个复杂度是 \(O(1)\) 的。

代码实现的话,就可以用一个 std :: vector ,按深度从大到小 ( \(maxdep[u] \to dep[u]\) )存储每个点的信息,因为这样最方便继承重儿子状态(每次加入状态只在整个 vector 末端添加一个元素)

其实可以动态开内存,顺着做,但我似乎学不来

常数似乎有点大,没比 \(O(n \log n)\) 快多少,vector 用多了... Wearry 到是优化了点常数到了 \(4000+ ms\) 。

话说这个很像原来 DOFY 讲过的那道 Dsu on Tree

代码

#include <bits/stdc++.h>#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)using namespace std;typedef long long ll;inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}inline int read() {int x = 0, fh = 1; char ch = getchar();for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);return x * fh;
}void File() {
#ifdef zjp_shadowfreopen ("3653.in", "r", stdin);freopen ("3653.out", "w", stdout);
#endif
}const int N = 3e5 + 1e3;struct Ask { int k, id; } ; vector<Ask> V[N];vector<int> G[N]; int sz[N], maxdep[N], dep[N], sonmaxdep[N], son[N], rt[N];vector<ll> sum[N]; int n, q; ll ans[N], Size = 0;void Dfs_Init(int u, int fa = 0) {maxdep[u] = dep[u] = dep[fa] + 1;For (i, 0, G[u].size() - 1) {register int v = G[u][i];if (v ^ fa) Dfs_Init(v, u), chkmax(maxdep[u], maxdep[v]);}}void Dfs(int u, int fa = 0) {For (i, 0, G[u].size() - 1) {int v = G[u][i];if (v == fa) continue ;Dfs(v, u); sz[u] += sz[v];if (maxdep[v] > maxdep[son[u]]) son[u] = v;}rt[u] = rt[son[u]]; if (!rt[u]) rt[u] = ++ Size;int len = (int)sum[rt[u]].size();ll Last = len ? sum[rt[u]][len - 1] : 0;sum[rt[u]].push_back(Last);if (son[u]) {For (i, 0, G[u].size() - 1) {int v = G[u][i]; if (v == fa || v == son[u]) continue ;For (j, 0, sum[rt[v]].size() - 1) {int nowdep = (maxdep[son[u]] - maxdep[v]) + j;sum[rt[u]][nowdep] += sum[rt[v]][j];}sum[rt[u]][len] += sum[rt[v]][sum[rt[v]].size() - 1];}}For (i, 0, V[u].size() - 1) {Ask now = V[u][i];ans[now.id] = sum[rt[u]][len];if (len > now.k) ans[now.id] -= sum[rt[u]][len - now.k - 1];ans[now.id] += 1ll * min(dep[u] - 1, now.k) * sz[u];}sum[rt[u]][len] += sz[u]; ++ sz[u];}int main () {File();n = read(); q = read();For (i, 1, n - 1) {int u = read(), v = read();G[u].push_back(v);G[v].push_back(u);}For (i, 1, q) {int p = read(), k = read();V[p].push_back((Ask) {k, i});}Dfs_Init(1); Dfs(1);For (i, 1, q)printf ("%lld\n", ans[i]);return 0;
}

转载于:https://www.cnblogs.com/zjp-shadow/p/9262234.html

BZOJ 3653: 谈笑风生(离线, 长链剖分, 后缀和)相关推荐

  1. 【十二省联考2019】希望【点边容斥】【换根dp】【长链剖分】【线性数据结构】【回退数据结构】【离线逆元】

    题意:给一棵树,两个参数 k,Lk,Lk,L,需要选择 kkk 个连通块,使得这 kkk 个连通块存在一个公共点,且该公共点到 kkk 个连通块内的任意一点的距离不超过 LLL,求方案数 模 9982 ...

  2. [BZOJ3653][长链剖分]谈笑风生

    BZOJ3653 我也不知道题面的引申意义 发现可以求出以每个点为p时的ans,讨论一下祖先和子孙的贡献,用长链剖分维护就好了 Code: #include<bits/stdc++.h> ...

  3. 【BZOJ】4543: [POI2014]Hotel加强版-长链剖分DP

    传送门:bzoj4543 题解 三点两两距离相等的情况如下图: 设 f [ i ] [ j ] f[i][j] f[i][j]表示 i i i子树中与 i i i距离为 j j j(相对深度为 j j ...

  4. UOJ284 快乐游戏鸡(树上动态规划问题、长链剖分+单调栈)

    Description 一棵 n 个点的有根树,带点权 wi. 从 s 出发,希望达到 t,每秒可以从当前点移动到某一个儿子. 有一个死亡次数,初始为 0.若在某个点 i(i != s, t) 时,死 ...

  5. 【刷题】BZOJ 3653 谈笑风生

    Description 设T 为一棵有根树,我们做如下的定义: ? 设a和b为T 中的两个不同节点.如果a是b的祖先,那么称"a比b不知道 高明到哪里去了". ? 设a 和 b 为 ...

  6. 对LCA、树上倍增、树链剖分(重链剖分长链剖分)和LCT(Link-Cut Tree)的学习

    LCA what is LCA & what can LCA do LCA(Lowest Common Ancestors),即最近公共祖先 在一棵树上,两个节点的深度最浅的公共祖先就是 L ...

  7. 树链剖分 + 后缀数组 - E. Misha and LCP on Tree

    E. Misha and LCP on Tree Problem's Link Mean: 给出一棵树,每个结点上有一个字母.每个询问给出两个路径,问这两个路径的串的最长公共前缀. analyse: ...

  8. Bzoj4016/洛谷P2993 [FJOI2014] 最短路径树问题(最短路径问题+长链剖分/点分治)

    题面 Bzoj 洛谷 题解 首先把最短路径树建出来(用\(Dijkstra\),没试过\(SPFA\)\(\leftarrow\)它死了),然后问题就变成了一个关于深度的问题,可以用长链剖分做,所以我 ...

  9. UOJ#284-快乐游戏鸡【长链剖分,线段树】

    正题 题目链接:https://uoj.ac/problem/284 题目大意 nnn个点的一棵树,每个点有一个wiw_iwi​表示至少死亡wiw_iwi​次才能通过这个点,否则就会死亡.只能往子节点 ...

最新文章

  1. 报名 | IBM苏中:从深蓝到AlphaGo,从大数据到认知商业
  2. Python中的reduce
  3. java+JBroFuzz对restful api进行fuzz测试
  4. Gridview 隐藏列
  5. SIGGRAPH 2021丨OPPO与南大提出双流网络:仅输入单张图片,就能“看”出物体材质...
  6. 三角形已知边长求高公式_五年级数学:“长方形、正方形、三角形、平行四边形、梯形” 知识重点、难点全解析,附习题!...
  7. bitcoin jsonrpc java_BitcoinCore JSONRPC Java使用创建账号,获取余额,转账等等...
  8. 移动端canvas_web前端开发分享-css,js移动篇
  9. linux里用户权限:~$,/$,~#,/#的区别与含义
  10. Boring Old Menu Bar for Mac(Big Sur菜单栏优化工具)
  11. 搭建cacti 期间问题总结
  12. 不借助 vue-cli 工具自行搭建 vue 项目
  13. WikiExtractor.py(维基百科抽取器)
  14. 计蒜客 青出于蓝胜于蓝 dfs序+树状
  15. 将无线鼠标改造成有线鼠标
  16. 一作发表6篇论文,他博三才出科研成果,厚积薄发终获成功
  17. 数字基建系列(一)数分如何参与埋点工作
  18. 作为软件开发人员,3种简单的方法来推销自己
  19. 自我评价范文计算机专业,计算机专业求职自我评价范文
  20. Working Practice-召集相关人员面对面的沟通是处理问题最快的方式之一

热门文章

  1. linux系统fuser命令,Linux系统使用Fuser命令的方法
  2. 控制台资费管理主菜单java_java毕业设计_springboot框架的高速公路收费管理系统...
  3. html 输入框从左上角,在输入框的左上角,使文本开始_input_开发99编程知识库
  4. linux java jar war_简单介绍Java 的JAR包、EAR包、WAR包区别
  5. 信息隐藏将txt文件合并到jpg文件中_使用Kali Linux在图像内隐藏机密消息—可在任何Linux发行版使用
  6. java privilege的用法_java反射--注解的定义与运用以及权限拦截
  7. go 语言 first argument to append must be slice
  8. 16位的数字高字节和低字节_显示8位数字的较低和较高半字节的掩蔽| 8086微处理器...
  9. c++语句switch语句_错误:案例标签不在C中的switch语句内
  10. mysql gtid binlog_MySQL之-四步实现BinLog Replication升级为GTIDs Replication的代码实例