BZOJ 3653: 谈笑风生(离线, 长链剖分, 后缀和)
题意
给你一颗有 \(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\) 本身)
首先我们考虑两种情况:
- \(a\) 为 \(p\) 的祖先,那么这部分贡献很好计算,就是 \(\min\{dep[p] - 1,k\} \times sz[u]\) 。
- \(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_shadow
freopen ("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;
}
BZOJ 3653: 谈笑风生(离线, 长链剖分, 后缀和)的更多相关文章
- 【BZOJ3653】谈笑风生(长链剖分)
[BZOJ3653]谈笑风生(长链剖分) 题面 BZOJ 洛谷 权限题啊.... 题解 首先根据题目给的条件,发现\(a,b\)都要是\(c\)的父亲. 所以这三个点是树上的一条深度单增的链. 因为\ ...
- 2019.01.19 bzoj3653: 谈笑风生(长链剖分优化dp)
传送门 长链剖分优化dpdpdp水题. 题意简述:给一棵树,mmm次询问,每次给一个点aaa和一个值kkk,询问满足如下条件的三元组(a,b,c)(a,b,c)(a,b,c)的个数. a,b是c的祖先 ...
- bzoj 3252: 攻略 -- 长链剖分+贪心
3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MB Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神 ...
- bzoj 3252 攻略 长链剖分思想+贪心
攻略 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 889 Solved: 423[Submit][Status][Discuss] Descrip ...
- BZOJ 3626 LCA(离线+树链剖分)
首先注意到这样一个事实. 树上两个点(u,v)的LCA的深度,可以转化为先将u到根路径点权都加1,然后求v到根路径上的总点权值. 并且该题支持离线.那么我们可以把一个区间询问拆成两个前缀和形式的询问. ...
- P5384[Cnoi2019]雪松果树 (长链剖分)
题面 一棵以 1 1 1 为根的 N N N 个节点的有根树, Q Q Q 次询问,每次问一个点 u u u 的 k k k 级兄弟有多少个(第 k k k 代祖先的第 k k k 代孩子),如果没有 ...
- BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)
BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...
- BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)
题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...
- BZOJ[3252]攻略(长链剖分)
BZOJ[3252]攻略 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏<XX半岛> ...
随机推荐
- 不指定源ip时,系统选择哪个ip作为ping包的源ip?
问题:当centos 有多个网口,发起ping包时,是根据什么规则来确定是使用哪个源ip? 解答:根据目的ip来确定,迭代可以确定源ip 具体的确定方法是, (1)先根据目的ip来确定使用哪个路由表项 ...
- 使用ethtool显示硬件PHY信息
1.总结: 使用ethtool 可以查看端口的phy配置 2.显示端口配置 [root@localhost zhou]# ethtool ens33Settings for ens33: Suppor ...
- arcgis for js学习之Graphic类
arcgis for js学习之Graphic类 <title>Graphic类</title> <meta charset="utf-8" /> ...
- java新知识系列 六
sleep和wait的区别有: Servlet方法的使用 方法重写的规则,以及两同两小一大原则: DispatcherServlet的解析 依赖注入DU和控制反转Ioc AOP和OOP的区别 Spri ...
- JHipster技术栈定制 - 基于UAA的微服务之间安全调用
本文通过代码实例演示如何通过UAA实现微服务之间的安全调用. uaa: 身份认证服务,同时也作为被调用的资源服务.服务端口9999. microservice1: 调用uaa的消费者服务,服务端口80 ...
- MySQL 修改账号的IP限制条件
今天遇到一个需求:修改MySQL用户的权限,需要限制特定IP地址才能访问,第一次遇到这类需求,结果在测试过程,使用更新系统权限报发现出现了一些问题, 具体演示如下. 下面测试环境为MySQL 5.6. ...
- Spring MVC中用@ResponseBody转json,对json进行处理方法汇总
<mvc:annotation-driven> <mvc:message-converters register-defaults="true"> < ...
- 【原】无脑操作:eclipse创建maven工程时,如何修改默认JDK版本?
问题描述:eclipse建立maven项目时,JDK版本默认是1.5,想创建时默认版本设置为1.8,如何修改? 解决方案: 找到本机maven仓库存放位置,比如:${user.home}/.m2/路径 ...
- .NET CORE学习笔记系列(2)——依赖注入[4]: 创建一个简易版的DI框架[上篇]
原文https://www.cnblogs.com/artech/p/net-core-di-04.html 本系列文章旨在剖析.NET Core的依赖注入框架的实现原理,到目前为止我们通过三篇文章从 ...
- Push rejected: Push to origin/master was rejected错误解决方案
解决方案如下: 1.切换到自己项目所在的目录,右键选择GIT BASH Here,Idea中可使用Alt+F12 2.在terminl窗口中依次输入命令: git pull git pull orig ...