【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT
题目描述
强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。
输入
共有 n + 2 行。
第一行包含一个正整数,表示测试点编号。
第二行包含一个正整数 n ,表示总共要加入的节点数。
我们令加入节点前的总共朋友对数是 last_ans,在一开始时它的值为0。
接下来 n 行中第 i 行有三个数 ai, bi, ri,表示节点 i 的父节点的编号为 ai xor (last_ans mod 10^9) (其中xor 表示异或,mod 表示取余,数据保证这样操作后得到的结果介于 1到i – 1之间),与父节点之间的边权为 ci,节点 i 上小精灵的感受能力值为r!。
注意 a1 = c1 = 0,表示 1 号点是根节点,对于 i > 1,父节点的编号至少为1。
输出
包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。
样例输入
0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4
样例输出
0
1
2
4
7
题解
替罪点分树套SBT
如果不考虑插入新点位置(即可以离线把树建出来),只考虑查询的话,那么就相当于查询$dis(i,j)\ge r_i+r_j$,即$dis(i,j)-r_j\ge r_i$的点$j$有多少个。
考虑点分治,一个点的子树中某子节点子树以外的节点到该子节点子树中某点的距离,可以看作先到根节点,再从根节点到这个点。所以可以使用容斥的方法,用所有的点减去在该子树内的点,维护某子树中所有点的$dis(x,rt)-r$,和某子树中所有点的$dis(x,fa[rt])-r$,在查找时,即找出$dis-r\ge r_i-dis(i,rt)$的所有点,于是使用平衡树SBT维护这两个信息,并在平衡树里查找即可。
然而本题强制在线,因此不能把整棵点分树建出来然后处理询问。
这时就可以使用替罪羊树“替罪”的思想,当距离偏差大的时候暴力重构。这样就可以维护一个近似平衡的分治结构以处理询问。
对于每个加点操作,直接把它挂到父亲节点之下,并向上更新子树信息。如果失衡,则找到最上端的替罪节点,重构这棵树。由于点分树的子树就是原树的连通块结构,因此可以直接对这部分建出点分树以重构。
实现起来稍微有点复杂,可以使用vector什么的减小代码复杂度。
查询时按照上面的方法查询即可,需要使用倍增LCA求两点距离。
时间复杂度$O(n\log^3n)=O(能过)$
还是需要内存回收之类的保证空间复杂度。
#include <queue>
#include <cstdio>
#include <vector>
#include <algorithm>
#define N 100010
using namespace std;
struct sbt
{
int w , ls , rs , si;
}a[20000010];
queue<int> q;
vector<int> c[N];
int head[N] , to[N << 1] , next[N << 1] , cnt , fa[N][20] , len[N] , deep[N] , log[N];
int r[N] , pre[N] , si[N] , mx[N] , sum , root , ra[N] , rb[N] , vis[N];
long long ans;
void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void zig(int &k)
{
int t = a[k].ls;
a[k].ls = a[t].rs , a[t].rs = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1;
k = t;
}
void zag(int &k)
{
int t = a[k].rs;
a[k].rs = a[t].ls , a[t].ls = k , a[t].si = a[k].si , a[k].si = a[a[k].ls].si + a[a[k].rs].si + 1;
k = t;
}
void maintain(int &k , bool flag)
{
if(!flag)
{
if(a[a[a[k].ls].ls].si > a[a[k].rs].si) zig(k);
else if(a[a[a[k].ls].rs].si > a[a[k].rs].si) zag(a[k].ls) , zig(k);
else return;
}
else
{
if(a[a[a[k].rs].rs].si > a[a[k].ls].si) zag(k);
else if(a[a[a[k].rs].ls].si > a[a[k].ls].si) zig(a[k].rs) , zag(k);
else return;
}
maintain(a[k].ls , 0) , maintain(a[k].rs , 1);
maintain(k , 0) , maintain(k , 1);
}
void insert(int &k , int x)
{
if(!k) k = q.front() , q.pop() , a[k].w = x , a[k].si = 1;
else
{
a[k].si ++ ;
if(x < a[k].w) insert(a[k].ls , x);
else insert(a[k].rs , x);
maintain(k , x >= a[k].w);
}
}
int find(int k , int x)
{
if(!k) return 0;
else if(x <= a[k].w) return find(a[k].ls , x) + a[a[k].rs].si + 1;
else return find(a[k].rs , x);
}
void del(int &k)
{
if(!k) return;
del(a[k].ls) , del(a[k].rs);
a[k].w = a[k].si = 0 , q.push(k) , k = 0;
}
int lca(int x , int y)
{
int i;
if(len[x] < len[y]) swap(x , y);
for(i = log[len[x] - len[y]] ; ~i ; i -- )
if(len[x] - len[y] >= (1 << i))
x = fa[x][i];
if(x == y) return x;
for(i = log[len[x]] ; ~i ; i -- )
if(len[x] >= (1 << i) && fa[x][i] != fa[y][i])
x = fa[x][i] , y = fa[y][i];
return fa[x][0];
}
int dis(int x , int y)
{
return deep[x] + deep[y] - 2 * deep[lca(x , y)];
}
int query(int x)
{
int i , ans = 0;
for(i = x ; i ; i = pre[i])
ans += find(ra[i] , dis(x , i) - r[x]);
for(i = x ; pre[i] ; i = pre[i])
ans -= find(rb[i] , dis(x , pre[i]) - r[x]);
return ans;
}
void dfs(int x , int fa , int p)
{
int i;
si[x] = 1 , c[p].push_back(x) , insert(ra[p] , r[x] - dis(x , p));
if(pre[p]) insert(rb[p] , r[x] - dis(x , pre[p]));
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]] && to[i] != fa)
dfs(to[i] , x , p) , si[x] += si[to[i]];
}
void getroot(int x , int fa)
{
int i;
si[x] = 1 , mx[x] = 0;
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]] && to[i] != fa)
getroot(to[i] , x) , si[x] += si[to[i]] , mx[x] = max(mx[x] , si[to[i]]);
mx[x] = max(mx[x] , sum - si[x]);
if(mx[x] < mx[root]) root = x;
}
void solve(int x , int from)
{
int i;
del(ra[x]) , del(rb[x]) , c[x].clear();
pre[x] = from , vis[x] = 1 , dfs(x , 0 , x);
for(i = head[x] ; i ; i = next[i])
if(!vis[to[i]])
sum = si[to[i]] , root = 0 , getroot(to[i] , 0) , solve(root , x);
}
void join(int x)
{
int i , last = -1;
for(i = x ; i ; i = pre[i])
{
c[i].push_back(x) , insert(ra[i] , r[x] - dis(x , i));
if(pre[i]) insert(rb[i] , r[x] - dis(x , pre[i]));
si[i] ++ ;
if(pre[i] && si[i] * 4 > (si[pre[i]] + 1) * 3) last = pre[i];
}
if(~last)
{
for(i = 0 ; i < (int)c[last].size() ; i ++ ) vis[c[last][i]] = 0;
sum = si[last] , root = 0 , getroot(last , 0) , solve(root , pre[last]);
}
}
int main()
{
int n , i , j , f , b;
for(i = 1 ; i <= 20000000 ; i ++ ) q.push(i);
scanf("%*d%d%*d%*d%d" , &n , &r[1]);
puts("0");
insert(ra[1] , r[1]) , c[1].push_back(1) , si[1] = vis[1] = 1 , mx[0] = 1 << 30;
for(i = 2 ; i <= n ; i ++ )
{
scanf("%d%d%d" , &f , &b , &r[i]) , f ^= (ans % 1000000000) , log[i] = log[i >> 1] + 1 , vis[i] = 1;
fa[i][0] = pre[i] = f , len[i] = len[f] + 1 , deep[i] = deep[f] + b;
add(f , i) , add(i , f);
for(j = 1 ; (1 << j) <= len[i] ; j ++ ) fa[i][j] = fa[fa[i][j - 1]][j - 1];
ans += query(i) , printf("%lld\n" , ans);
join(i);
}
return 0;
}
【bzoj3435】[Wc2014]紫荆花之恋 替罪点分树套SBT的更多相关文章
- 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT
[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...
- BZOJ3435[Wc2014]紫荆花之恋——动态点分治(替罪羊式点分树套替罪羊树)
题目描述 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是一个带权树.每 ...
- BZOJ3435: [Wc2014]紫荆花之恋(替罪羊树,Treap)
Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来.仔细看看的话,这个大树实际上是 ...
- bzoj3435 [Wc2014]紫荆花之恋
如果这棵树不变的话,就是一个裸的点分树套平衡树,式子也很好推$di+dj<=ri+rj$,$ri-di>=dj-rj$ 平衡树维护$dj-rj$,然后查$ri-di$的$rank$即可. ...
- bzoj3435 [Wc2014]紫荆花之恋(动态点分治+替罪羊树)
传送门(权限) 传送门(非权限) 题解 我终终终终终终于做出来啦!!! 作为一个没有学过替罪羊树的蒟蒻现场学了一下替罪羊树,作为一个平衡树都写数组版本的看着大佬的指针题解无语只能硬去理解然后照着抄了一 ...
- luoguP3920 [WC2014]紫荆花之恋 动态点分治 + 替罪羊树
意外的好写..... 考虑点分 \(dis(i, j) \leq r_i + r_j\) 对于过分治中心一点\(u\),有 \(dis(i, u) - r_i = dis(j, u) + r_j\) ...
- bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400
3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec Memory Limit: 512 MBSubmit: 159 Solved: 40[Submit][Status] ...
- BZOJ 3435: [Wc2014]紫荆花之恋
二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...
- BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋
https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...
随机推荐
- pytho线程信号量
pytho线程信号量 import threading,time def going(num,sleep_time): semaphore.acquire()#启动允许执行 print("g ...
- Solaris&&QNX® Neutrino®&&OpenVMS&&FreeBSD&&AIX
原文链接Solaris (读作 /se'laris:/ 或者 /so'le:ris/ 或者 '梭拉瑞斯' )是Sun Microsystems研发的计算机 操作系统.它被认为是UNIX操作系统的衍生版 ...
- Python中的集合set
>>> help(set) Help on class set in module __builtin__: class set(object) | set(iterable) -- ...
- css3媒体查询中device-width和width的区别
1.device-width 定义:定义输出设备的屏幕可见宽度. 不管你的网页是在safari打开还是嵌在某个webview中,device-width都只跟你的设备有关,如果是同一个设备,那么他的值 ...
- Oracle Undo 和 Redo
1. REDO(重做信息) Redo log file(重做日志文件),是数据库的事务日志. Oracle维护着两类重做日志文件:在线(online)重做日志文件和归档(archived)重做日志文件 ...
- 梁勇 java教材 编程练习题 第二章 2.6 键盘 读取一个在0 到 9999 之间的整数,并将该整数的各位数字相加。
import java.util.Scanner; /** * 需求:从键盘输入任意0~9999之间的整数,输出这个整数各位数字之和. * 思路:方法一,使用扫描器Scanner类,扫描控制台输入流 ...
- oc描述器排序
int main(int argc, const char * argv[]) { @autoreleasepool { NSArray *array = @[CreateDict(@"王思 ...
- JS时间格式和时间戳的相互转换
时间戳转化为日期的方式 ; var newDate = new Date(); newDate.setTime(timestamp * ); // Mon May 28 2018 console.lo ...
- 快速搭建lvs + keepalived + nginx
环境: VIP 192.168.2.224 LVS 192.168.2.217 centos7 nginx1 192.168.2.231 c ...
- nginx修改nginx.conf配置可以https访问
修改nginx.conf,参照如下更改配置server { listen 443; server_name abc.com; // 访问域名 ssl on; root /var/www/bjubi.c ...