洛谷 P3920 [WC2014]紫荆花之恋
想题5分钟,调题20小时\(QAQ\)
我做这题的历程:
这什么破玩意儿
如果您听说过「替罪羊式重构点分树」的话这题就很好想了。
首先考虑朴素的点分治:
每次分治统计路径经过分治中心的点对的贡献。
记\(dis_i\)为点\(i\)到分治中心的距离,条件即\(dis_i+dis_j\le r_i+r_j\)。
移个项:\(dis_i-r_i\le r_j-dis_j\)。
把\(r_i-dis_i\)插进平衡树里,查询平衡树中大于等于\(dis_i-r_i\)的数量。
动态的做法就很明显了,点分树上每个点开个平衡树。还要容斥减掉同一子树的贡献,用\(vector\)记录每层上用于容斥的平衡树。
加点直接在点分树上将\(i\)与\(a_i\)连边,暴力跳点分树在平衡树里查,并把自己的信息插进去。
注意到每次都会在某个点下方挂一个点,而点分树的树高要维持在\(\log\)级别。
很像平衡树。旋转是不可能了,利用替罪羊树的思想重构点分树。
记\(siz\)为点分树上子树大小。当\(\max\limits_{i\in son(x)}siz_i\ge siz_x\times \alpha\)(\(\alpha\)为平衡因子,我定的\(0.7\))就重构点\(i\)的子树。
\(asuldb\):重构多简单,不就点分治一下吗。
重构我们要干啥(\(x\)为待重构点):
- 维护点分树上每个点的儿子,\(bfs\)得到要重构哪些点
- 清空每个点的平衡树
- 保留非重构点的容斥平衡树,清空其他的。注意到容斥的\(vector\)记录的平衡树在点分树上所属的点深度是递增的。一直
pop_back
并清空平衡树直到末尾元素为\(fa(x)\)即可。 - 点分治重构点分树
为了保证单次重构复杂度是\(O(siz_x\log siz_x)\)的,把子树信息提出来排个序直接build
。\(treap\)有随机权值不好build
,\(splay\)太慢于是选择替罪羊树。
(其实假了,还有个排序重构是\(O(siz_x\log^2 siz_x)\)的。。。)
还要带加点查询点对的距离,可以倍增,我用的\(LCT\)。
复杂度为\(O(n\log^2 n)\)。
注意垃圾回收。
全是细节的代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <set>
#define maxn 100005
#define inf 0x3f3f3f3f
const double alpha = 0.7;
const int mod = 1e9;
using namespace std;
inline int read(){
int x=0,y=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')y=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return y?-x:x;
}
struct edge{
int pre,to,l;
}e[maxn<<1];
struct ScapeGoatTree{
#define ls(x) son[x][0]
#define rs(x) son[x][1]
#define son(x,y) son[x][y]
int son[maxn*50][2],dat[maxn*50],siz[maxn*50],pool[maxn*50],a[maxn<<1],len,cnt;
bool isbad(int node){
return 1.0*max(siz[ls(node)],siz[rs(node)])>=alpha*siz[node];
}
void build(int *base,int l,int r,int &node){
if(l>r){node=0;return;}
node=pool[cnt--];
int mid=l+r>>1;
dat[node]=base[mid];
build(base,l,mid-1,ls(node));
build(base,mid+1,r,rs(node));
siz[node]=siz[ls(node)]+siz[rs(node)]+1;
}
void dfs(int node){
if(!node)return;
dfs(ls(node));
a[++len]=dat[node];
dfs(rs(node));
pool[++cnt]=node;
}
int push(int &node,int d){
if(!node){
dat[node=pool[cnt--]]=d,siz[node]=1;
ls(node)=rs(node)=0;
return 0;
}
++siz[node];
int p=push(son(node,dat[node]<d),d);
if(isbad(node))return node;
return p;
}
void clear(int &node){
if(!node)return;
clear(ls(node)),clear(rs(node));
pool[++cnt]=node,node=0;
}
void insert(int &node,int d){
int p=push(node,d);
if(p)len=0,dfs(p),build(a,1,len,p);
}
int find(int node,int x){
int ans=0;
while(node){
if(dat[node]>=x)ans+=siz[rs(node)]+1,node=ls(node);
else node=rs(node);
}
return ans;
}
ScapeGoatTree(){
cnt=maxn*50-1;
for(register int i=1;i<=cnt;++i)pool[i]=i;
}
}sgt;
struct LinkCutTree{
#define whson(x) (son(fa[x],1)==x)
#define isroot(x) (son(fa[x],0)!=x&&son(fa[x],1)!=x)
int son[maxn<<1][2],fa[maxn<<1],sum[maxn<<1],v[maxn<<1],rev[maxn<<1],sta[maxn<<1];
inline void update(int node){
sum[node]=sum[ls(node)]+sum[rs(node)]+v[node];
}
inline void addedge(int s,int f,int wh){
if(s)fa[s]=f;
son(f,wh)=s;
}
inline void reverdown(int node){
swap(ls(node),rs(node)),rev[node]^=1;
}
inline void pushdown(int node){
if(rev[node])reverdown(ls(node)),reverdown(rs(node)),rev[node]=0;
}
void zhuan(int x){
int f=fa[x],gf=fa[f],wh=whson(x);
fa[x]=gf;
if(!isroot(f))son(gf,whson(f))=x;
addedge(son(x,wh^1),f,wh);
addedge(f,x,wh^1);
update(f),update(x);
}
void splay(int x){
int y=x,top=1;
sta[1]=x;
while(!isroot(y))sta[++top]=y=fa[y];
while(top)pushdown(sta[top--]);
while(!isroot(x)){
y=fa[x];
if(!isroot(y))zhuan(whson(y)^whson(x)?x:y);
zhuan(x);
}
}
void access(int x){
for(register int y=0;x;y=x,x=fa[x])
splay(x),son(x,1)=y,update(x);
}
void makeroot(int x){
access(x),splay(x),reverdown(x);
}
void link(int x,int y){fa[x]=y;}
int Get(int x,int y){
makeroot(x),access(y),splay(y);
return sum[y];
}
}lct;
int r1[maxn],r2[maxn],h[maxn],siz[maxn],ts[maxn],pool[maxn<<2],num,cnt=(maxn<<2)-1,all,mx,root,l1,l2;
int mp[maxn],rt[maxn],exc[maxn<<3],line[maxn],dep[maxn],fa[maxn],a[maxn];
bool vis[maxn],rub[maxn<<1];
vector<int>b[maxn];
set<int>son[maxn];
inline void add(int from,int to,int l){
e[++num].pre=h[from],h[from]=num,e[num].to=to,e[num].l=l;
}
void getroot(int node,int f=0){
int ma=0,x;
siz[node]=1;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(x==f||vis[x])continue;
getroot(x,node),ma=max(ma,siz[x]),siz[node]+=siz[x];
}
ma=max(ma,all-siz[node]);
if(ma<mx)mx=ma,root=node;
}
void bfs(int node){
int head=0,tail=1,rc=cnt;
dep[line[1]=node]=0;
while(head<tail){
int x=line[++head];
vis[x]=0;
for(set<int>::iterator iter=son[x].begin();iter!=son[x].end();++iter)
dep[line[++tail]=*iter]=dep[x]+1;
while(dep[x]--){
sgt.clear(exc[*b[x].rbegin()]);
if(!rub[*b[x].rbegin()])rub[pool[++cnt]=*b[x].rbegin()]=1;
b[x].pop_back();
}
sgt.clear(rt[x]);
son[x].clear();
}
for(register int i=rc+1;i<=cnt;++i)rub[pool[i]]=0;
}
void dfs(int node,int len,int f){
r1[++l1]=r2[++l2]=a[node]-len,b[node].push_back(pool[cnt]);
siz[node]=1;
int x;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(x==f||vis[x])continue;
dfs(x,len+e[i].l,node),siz[node]+=siz[x];
}
}
void build(int node){
vis[node]=ts[node]=1,mp[node]=0,r1[l1=1]=a[node];
int x;
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(vis[x])continue;
l2=0,dfs(x,e[i].l,node);
sort(r2+1,r2+1+l2);
sgt.build(r2,1,l2,exc[pool[cnt--]]);
}
sort(r1+1,r1+1+l1);
sgt.build(r1,1,l1,rt[node]);
for(register int i=h[node];i;i=e[i].pre){
x=e[i].to;
if(vis[x])continue;
mx=inf,mp[node]=max(all=siz[x],mp[node]),getroot(x);
ts[node]+=all,fa[root]=node,son[node].insert(root);
build(root);
}
}
void rebuild(int node){
int x=node,p=0;
while(x){
mp[fa[x]]=max(mp[fa[x]],++ts[x]);
if(1.0*mp[x]>=alpha*ts[x])p=x;
x=fa[x];
}
if(p){
bfs(p);
mx=inf,all=ts[p],getroot(p);
fa[root]=fa[p],son[fa[p]].erase(p),son[fa[p]].insert(root);
build(root);
}
}
int main(){
memset(vis,1,sizeof vis);
for(register int i=1;i<=cnt;++i)pool[i]=i;
read();
int n=read(),x,y,node;
long long ans=0;
read(),read();
ts[1]=1,sgt.insert(rt[1],a[1]=read()),puts("0");
for(register int i=2;i<=n;++i){
x=read<long long>()^(ans%mod),lct.v[n+i]=y=read(),a[i]=read();
fa[i]=node=x,son[x].insert(i),sgt.insert(rt[i],a[i]);
add(i,x,y),add(x,i,y),lct.link(n+i,x),lct.link(i,n+i);
b[i]=b[x],b[i].push_back(pool[cnt--]);
for(register int j=b[i].size()-1;node;node=fa[node],--j){
int dis=lct.Get(i,node);
ans+=sgt.find(rt[node],dis-a[i])-sgt.find(exc[b[i][j]],dis-a[i]);
sgt.insert(rt[node],a[i]-dis),sgt.insert(exc[b[i][j]],a[i]-dis);
}
printf("%lld\n",ans);
rebuild(i);
}
}