[UOJ55]紫荆花之恋
第一次打“真正的”动态点分
如果树是静态的,直接点分:用$d_x$代表$x$到分治中心的距离,限制条件即为$d_i+d_j\leq r_i+r_j$,考虑枚举$j$,那么我们要查询有多少满足$d_i-r_i\leq r_j-d_j$的$i$,用平衡树维护即可
现在树是动态的,那么每次我们往点分树中加一个叶子,先更新答案再更新平衡树即可,每个点分树中的点存两棵平衡树,一棵存以这个点为dfs起点的$d_i-r_i$,另一棵存以(它父亲到它管辖范围的第一个点)为dfs起点的$d_i-r_i$,查询时容斥一下就可以了
但直接加叶子会造成点分树不平衡,所以这里用替罪羊树的思想重构,这样就能保证时间复杂度
实现起来还是需要一点技巧的,如果要重构点分树中的一个点$x$,那么在原树中dfs时只访问那些在点分树中比$x$深的点就对应着$x$的点分树子树了,平衡树使用旋转treap,重构时先内存回收,再排序后$O(n)$建treap,这样会快一些(好吧主要是我写的常数太大...)
#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#include<assert.h>
using namespace std;
typedef long long ll;
const int inf=2147483647;
ll ans;
int r[100010],*d,*r1,*r2,*sz,*df;
namespace tree{
int h[100010],nex[200010],to[200010],v[200010],M;
void ins(int a,int b,int c){
M++;
to[M]=b;
v[M]=c;
nex[M]=h[a];
h[a]=M;
}
int fa[100010][17],dep[100010],dis[100010];
void add(int a,int b,int c){
ins(a,b,c);
ins(b,a,c);
fa[b][0]=a;
dep[b]=dep[a]+1;
dis[b]=dis[a]+c;
for(int i=1;i<17;i++)fa[b][i]=fa[fa[b][i-1]][i-1];
}
int lca(int x,int y){
int i;
if(dep[x]<dep[y])swap(x,y);
for(i=16;i>=0;i--){
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
}
if(x==y)return x;
for(i=16;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int vis[100010],siz[100010],lim,C;
#define ok vis[to[i]]!=C&&to[i]!=fa&&d[to[i]]>=lim
void dfs1(int fa,int x){
siz[x]=1;
for(int i=h[x];i;i=nex[i]){
if(ok){
dfs1(x,to[i]);
siz[x]+=siz[to[i]];
}
}
}
int mn,cn,n;
void dfs2(int fa,int x){
int i,k=0;
for(i=h[x];i;i=nex[i]){
if(ok){
dfs2(x,to[i]);
k=max(k,siz[to[i]]);
}
}
k=max(k,n-siz[x]);
if(k<mn){
mn=k;
cn=x;
}
}
}
int getdis(int x,int y){
using namespace tree;
return dis[x]+dis[y]-dis[lca(x,y)]*2;
}
namespace treap{
int fa[4000010],ch[4000010][2],fix[4000010],s[4000010],v[4000010],st[4000010],tp,M;
#define l(x) ch[x][0]
#define r(x) ch[x][1]
int node(int d){
int x;
if(tp){
x=st[tp--];
fa[x]=l(x)=r(x)=0;
}else
x=++M;
fix[x]=rand();
v[x]=d;
s[x]=1;
return x;
}
void pushup(int x){
s[x]=s[l(x)]+s[r(x)]+1;
}
void rot(int x){
int y,z,f,b;
y=fa[x];
z=fa[y];
f=ch[y][0]==x;
b=ch[x][f];
fa[x]=z;
fa[y]=x;
if(b)fa[b]=y;
ch[x][f]=y;
ch[y][f^1]=b;
if(ch[z][0]==y)ch[z][0]=x;
if(ch[z][1]==y)ch[z][1]=x;
pushup(y);
pushup(x);
}
int insert(int&x,int d){
if(x==0)return x=node(d);
int k;
if(d<=v[x]){
k=insert(l(x),d);
if(!fa[l(x)])fa[l(x)]=x;
}else{
k=insert(r(x),d);
if(!fa[r(x)])fa[r(x)]=x;
}
pushup(x);
return k;
}
void ins(int&x,int d){
int k=insert(x,d);
while(fa[k]&&fix[k]>fix[fa[k]])rot(k);
while(fa[x])x=fa[x];
}
int query(int x,int d){
if(x==0)return 0;
if(d>=v[x])return s[l(x)]+1+query(r(x),d);
return query(l(x),d);
}
void rec(int x){
if(!x)return;
st[++tp]=x;
rec(l(x));
rec(r(x));
}
int stk[100010],top;
int build(int*p,int n){
int x,las,i;
sort(p+1,p+n+1);
top=0;
for(i=1;i<=n;i++){
x=node(p[i]);
las=0;
while(top&&fix[stk[top]]<fix[x]){
pushup(stk[top]);
las=stk[top--];
}
if(top)r(stk[top])=x;
l(x)=las;
stk[++top]=x;
}
while(top)pushup(stk[top--]);
return stk[1];
}
}
void drec(int fa,int x){
using namespace tree;
using namespace treap;
rec(r1[x]);
r1[x]=0;
rec(r2[x]);
r2[x]=0;
for(int i=h[x];i;i=nex[i]){
if(to[i]!=fa&&d[to[i]]>=lim)drec(x,to[i]);
}
}
int p[100010],N,u;
void dfs3(int fa,int x){
using namespace tree;
p[++N]=getdis(x,u)-r[x];
for(int i=h[x];i;i=nex[i]){
if(ok)dfs3(x,to[i]);
}
}
int solve(int fa,int x){
using namespace tree;
dfs1(0,x);
mn=inf;
n=siz[x];
dfs2(0,x);
if(fa){
u=fa;
N=0;
dfs3(0,x);
r2[cn]=treap::build(p,N);
}
x=cn;
vis[x]=C;
df[x]=fa;
d[x]=d[fa]+1;
u=x;
N=0;
dfs3(0,x);
r1[x]=treap::build(p,N);
sz[x]=1;
for(int i=h[x];i;i=nex[i]){
if(ok)sz[x]+=sz[solve(x,to[i])];
}
return x;
}
void rebuild(int fa,int x){
using namespace tree;
lim=d[x];
drec(0,x);
C++;
solve(fa,x);
}
namespace dtree{
const double al=.85;
int fa[100010],rt1[100010],rt2[100010],siz[100010],dep[100010];
//rt1:root=self,rt2:root=fa->self_area_1st
void addnode(int x){
fa[x]=tree::fa[x][0];
dep[x]=dep[fa[x]]+1;
int u,t;
for(u=x;fa[u];u=fa[u]){
t=getdis(x,fa[u]);
ans+=treap::query(rt1[fa[u]],r[x]-t);
ans-=treap::query(rt2[u],r[x]-t);
}
treap::ins(rt1[x],-r[x]);
for(u=x;fa[u];u=fa[u]){
t=getdis(x,fa[u]);
treap::ins(rt1[fa[u]],t-r[x]);
treap::ins(rt2[u],t-r[x]);
}
for(u=x;u;u=fa[u])siz[u]++;
t=0;
for(u=x;fa[u];u=fa[u]){
if(siz[u]>al*siz[fa[u]])t=fa[u];
}
if(t)rebuild(fa[t],t);
}
}
int main(){
using namespace treap;
using namespace dtree;
srand(19260817);
int n,i,a,c;
d=dep;
r1=rt1;
r2=rt2;
sz=siz;
df=dtree::fa;
scanf("%d%d",&a,&n);
scanf("%d%d%d",&a,&c,r+1);
ins(rt1[1],-r[1]);
d[1]=1;
sz[1]=1;
puts("0");
for(i=2;i<=n;i++){
scanf("%d%d%d",&a,&c,r+i);
a^=(ans%1000000000);
tree::add(a,i,c);
addnode(i);
printf("%lld\n",ans);
}
}
[UOJ55]紫荆花之恋的更多相关文章
- bzoj 3435: [Wc2014]紫荆花之恋 替罪羊树维护点分治 && AC400
3435: [Wc2014]紫荆花之恋 Time Limit: 240 Sec Memory Limit: 512 MBSubmit: 159 Solved: 40[Submit][Status] ...
- 【BZOJ3435】[Wc2014]紫荆花之恋 替罪点分树+SBT
[BZOJ3435][Wc2014]紫荆花之恋 Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从 ...
- BZOJ 3435: [Wc2014]紫荆花之恋
二次联通门 : BZOJ 3435: [Wc2014]紫荆花之恋 二次联通门 : luogu P3920 [WC2014]紫荆花之恋 /* luogu P3920 [WC2014]紫荆花之恋 怀疑人生 ...
- luogu P3920 [WC2014]紫荆花之恋
LINK:紫荆花之恋 每次动态加入一个节点 统计 有多少个节点和当前节点的距离小于他们的权值和. 显然我们不能n^2暴力. 考虑一个简化版的问题 树已经给出 每次求某个节点和其他节点的贡献. 不难想到 ...
- 【WC2014】紫荆花之恋(替罪羊重构点分树 & 平衡树)
Description 若带点权.边权的树上一对 \((u, v)\) 为 friend,那么需要满足 \(\text{dist}(u, v) \le r_u + r_v\),其中 \(r_x\) 为 ...
- BZOJ3435 & 洛谷3920 & UOJ55:[WC2014]紫荆花之恋
https://www.lydsy.com/JudgeOnline/problem.php?id=3435 https://www.luogu.org/problemnew/show/P3920 ht ...
- 数据结构(平衡树,树分治,暴力重构):WC 2014 紫荆花之恋
[题目描述] 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这棵大树实际上是一个带权 ...
- [WC 2014]紫荆花之恋
Description 强强和萌萌是一对好朋友.有一天他们在外面闲逛,突然看到前方有一棵紫荆树.这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来. 仔细看看的话,这个大树实际上 ...
- UOJ#55. 【WC2014】紫荆花之恋 点分树 替罪羊树 平衡树 splay Treap
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ55.html 题解 做法还是挺容易想到的. 但是写的话…… 首先这种题如果只要求一棵树中的满足条件的点数( ...
随机推荐
- 洛谷 P3375 【模板】KMP字符串匹配
我这段时间因为字符串太差而被关了起来了(昨晚打cf不会处理字符串现场找大佬模板瞎搞,差点就凉了),所以决定好好补一下字符串的知识QAQ,暂时先学习kmp算法吧~ 题目链接:https://www.lu ...
- 11个让你吃惊的linux命令
我已经用了十年的Linux了,通过今天这篇文章我将向大家展示一系列的命令.工具和技巧,我希望一开始就有人告诉我这些,而不是曾在我成长道路上绊住我. AD: 我已经用了十年的Linux了,通过今天这篇文 ...
- Java 关于微信公众号支付总结附代码
很多朋友第一次做微信支付的时候都有蒙,但当你完整的做一次就会发现其实并没有那么难 业务流程和应用场景官网有详细的说明:https://pay.weixin.qq.com/wiki/doc/api/js ...
- mount/umount命令【转】
转自:http://www.cnblogs.com/qq78292959/archive/2012/03/06/2382334.html 如果想在运行的Linux下访问其它文件系统中的资源的话,就要用 ...
- java 查看运行时某个类文件所在jar的位置
在一些大型项目中,项目所依赖的库可能比较到,有时候也会出现库冲突的情况,曾经遇到过一种情况:一个第三方云存储提供了一个sdk,这个sdk本身依赖httpclient相关的包,然而对方却把httpcli ...
- c语言中数组,指针数组,数组指针,二维数组指针
1.数组和指针 ] = {,,,,};// 定义数组 // 1. 指针和数组的关系 int * pa = array; pa = array; // p[0] == *(p+0) == array[0 ...
- 【python】时间戳、字典列表排序
记录一下昨天学到的知识: 一.文件相关 文件追加:f = open("fname","a") 文件不存在时创建 二.时间戳相关 http://www.jb ...
- linux命令(9):route命令
查看路由表:route –n //添加到主机的路由 # route add –host 192.168.168.110 dev eth0 # route add –host 192.168.168.1 ...
- HDU-5384
Danganronpa Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Tot ...
- 《java并发编程实战》读书笔记7--线程池的使用
第8章 线程池的使用 8.1 在任务与执行策略之间的隐性耦合 虽然Executor框架为制定和修改执行策略都提供了相当大的灵活性,但并非所有的任务都适用所有的执行策略.有些类型的任务需要明确地指明执行 ...