2019 ICPC 南昌 K. Tree(树上启发式合并,平衡树 treap)
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019 ICPC 南昌 K. Tree(树上启发式合并,平衡树 treap)相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
题目链接
https://nanti.jisuanke.com/t/42586
Problem
给定一个 n n n 个点的树,以及参数 k k k,树上每个点均有一个权值 v a l i \\mathrm{val}_i vali。
询问树上有序对 ( x , y ) (x,y) (x,y) 的数量,满足以下条件:
- x x x 不等于 y y y
- x x x 不是 y y y 的祖先
- y y y 不是 x x x 的祖先
- x x x 和 y y y 之间的距离不超过 k k k
- v a l x + v a l y = 2 × v a l l c a ( x , y ) \\mathrm{val}_x+\\mathrm{val}_y=2\\times \\mathrm{val}_{\\mathrm{lca}(x,y)} valx+valy=2×vallca(x,y)
n , k ≤ 1 0 5 n,k\\le 10^5 n,k≤105, 0 ≤ v a l i ≤ n 0\\le \\mathrm{val}_i\\le n 0≤vali≤n。
Solution
树上统计问题,发现问题可以划分成子树的问题,显然考虑树上启发式合并。
树上两点间距离问题考虑分析 l c a ( x , y ) \\mathrm{lca}(x,y) lca(x,y),假设已知 l c a ( x , y ) = z \\mathrm{lca}(x,y)=z lca(x,y)=z,考虑统计以 z z z 的为根的子数中,对答案有贡献的点对 ( x , y ) (x,y) (x,y) 的数量。
我们在树上启发式合并枚举到第 i i i 课子树的时候,统计与前 i − 1 i-1 i−1 棵子树之间的贡献即可。
考虑从给定的五个条件出发分析。条件五: v a l x + v a l y = 2 × v a l l c a ( x , y ) = z \\mathrm{val}_x+\\mathrm{val}_y=2\\times \\mathrm{val}_{\\mathrm{lca}(x,y)=z} valx+valy=2×vallca(x,y)=z,显然此时 v a l x \\mathrm{val}_x valx 和 v a l z \\mathrm{val}_{z} valz 都已经确定了,那么我们就可以得到 v a l y = 2 × v a l z − v a l x \\mathrm{val}_y=2\\times \\mathrm{val}_z-\\mathrm{val}_x valy=2×valz−valx。
考虑条件四, x x x 和 y y y 之间的距离不超过 k k k。我们已经知道了 d x d_x dx 和他们的 l c a \\mathrm{lca} lca 的深度 d z d_z dz,显然可以得到 d y = k − d x + 2 × d z d_y=k-d_x+2\\times d_z dy=k−dx+2×dz。
综上所述,问题转化为了,求前 i − 1 i-1 i−1 子树中深度不超过 d y d_y dy 且点的权值为 v a l y \\mathrm{val}_y valy 的点的个数。
考虑 v a l ≤ 1 0 5 \\mathrm{val}\\le 10^5 val≤105,可以直接维护权值,对于每一个权值,建一个平衡树,即建立 n + 1 n+1 n+1 棵平衡树,以深度为下标序列,维护该权值的点在每个深度的点的个数。
考虑 dsu 深度最多 log n \\log n logn,开 1 0 5 10^5 105 个平衡树,每个平衡树最多只有 log n \\log n logn 个点,我们只需要维护一个大小为 n log n = 2 × 1 0 6 n\\log n=2\\times 10^6 nlogn=2×106 的平衡树即可。
时间复杂度 O ( n log n log n ) O(n\\log n \\log n) O(nlognlogn)
调了一个小时才A,真的蚌埠住了
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7, maxm = 1e7 + 7, INF = 0x3f3f3f3f;
ll ans;
int val[maxn];
int root[maxn];
int deep[maxn];
namespace treap
{
struct node{
int l, r;
int key;
int val;
int cnt;
int size;
}tr[maxm];
int root, idx;
void pushup(int p){
tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
}
int get_node(int key){
tr[ ++ idx].key = key;
tr[idx].val = rand();
tr[idx].cnt = tr[idx].size = 1;
return idx;
}
void zig(int &p){//右旋
int q = tr[p].l;
tr[p].l = tr[q].r;tr[q].r = p, p = q;
pushup(tr[p].r), pushup(p);
}
void zag(int &p){//左旋
int q = tr[p].r;
tr[p].r = tr[q].l;tr[q].l = p, p = q;
pushup(tr[p].l), pushup(p);
}
void my_insert(int &p, int key){
if(!p) p = get_node(key);
else if(tr[p].key == key) tr[p].cnt ++ ;
else if(tr[p].key > key){
my_insert(tr[p].l, key);
if(tr[tr[p].l].val > tr[p].val)
zig(p);
}
else {
my_insert(tr[p].r, key);
if(tr[tr[p].r].val > tr[p].val)
zag(p);
}
pushup(p);
}
void my_delet_one(int &p, int key){
if(!p) return ;
if(tr[p].key == key){
if(tr[p].cnt > 1) tr[p].cnt -- ;
else if(tr[p].l || tr[p].r){
if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val){
zig(p);
my_delet_one(tr[p].r, key);
}
2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树
2019年ICPC南昌网络赛 J. Distance on the tree(树链剖分+主席树 查询路径边权第k大)
2019 (CCPC-Final 2019) K. Russian Dolls on the Christmas(dsu one tree)