洛谷 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);
    }
}
知识兔
计算机