题解 P3920【[WC2014]紫荆花之恋】

$\text{Link}$

题意

给 $n$ 次操作,每次操作加入一个节点 $k$,给出 $k$ 的父亲、$k$ 与父亲之间的边权、$k$ 的点权 $r_k$,操作后求出 $|\{(i,j)|1\le i<j\le k\text{ and }r_i+r_j\ge\text{dis}(i,j)\}|$。强制在线。

$n\le 10^5$。

思路

首先思考一个弱化问题:给一棵树,求出答案。

我们可以思考点分治,设当前分治中心为 $u$。对于在不同子树中的 $i$ 和 $j$,$\text{dis}(i,j)=\text{dis}(i,u)+\text{dis}(j,u)$,则答案转为统计 $r_i+r_j\ge \text{dis}(i,u)+\text{dis}(u,j)$ 的对数。

我们将式子移项得到 $r_i-\text{dis}(i,u)\ge\text{dis}(u,j)-r_j$。我们可以使用一个数据结构,支持插入和求排名操作,可以直接使用平衡树,也可以离散化使用权值线段树、权值树状数组维护 $u$ 所有的 $\text{dis}(u,j)-r_j$,查询 $r_i-\text{dis}(i,u)$ 的排名即可。

考虑强制在线后怎么做,显然我们有点分树,可以动态维护点分治。

但是问题来了:我们动态加点,怎么维护点分树的形态呢?

考虑点分树的复杂度是怎么来的:点分树每个节点的父节点是上层分治取的重心,保证了点分树树高为 $O(\log n)$,使得操作复杂度为 $O(\log n)\times O(ds)$。我们可以首先向直接使用原树作为点分树的方向思考,树高 $O(n)$,显然不行,只能得到 $30\text{pts}$。考虑维护点分树上的子树大小,使用替罪羊树的思想,设定平衡因子 $\alpha$,当 $siz_i>siz_{f_i}\times \alpha$ 时暴力重构这颗点分树子树,此处重构指对这部分子树进行点分治建出点分树。

对于这个算法数据结构的选用提一句:由于强制在线的缘故,无法离散化使用树状数组、线段树,如果使用平衡树的话:重构中重新插入 $O(\log n)$,树高 $O(\log n)$,会重构 $O(\log n)$ 次,时间复杂度 $O(n\log^3n)$,再加上平衡树的大常数可能需要卡常。

我们可以使用一种定期重构的数据结构:由于只需要清空、插入、求排名,可以使用两个 vector $a$ 和 $b$,每次暴力在 $b$ 中插入,当 $b$ 的大小超过预订的阀值 $S$ 时,$O(n)$ 将 $b$ 合并进 $a$ 中,查询排名直接在两个 vectorupper_bound 即可。

算一下时间复杂度,询问 $O(n\log n)\times O(\log n)=O(n\log^2n)$,插入 $O(n\log^2nS+\dfrac{n^2\log^2n}{S})$,$S$ 取 $O(\sqrt n)$ 得复杂度 $O(n\sqrt n\log^2 n)$。看似复杂度变差了,但由于 vector 自带的超小常数,反而跑得挺快,完全不需要卡常。

不知道为什么 $\alpha$ 取 $0.95$ 跑得最快。

最后贴一下代码吧:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=1e5+10,S=350,INF=1e7+10;
const double alpha=0.95; 
//like the Scapegoat_Tree to rebuild
int head[N],sz[N],cnt,n,r[N];
bool vis[N];
struct Edge{
    int to,nxt,val;
}a[N<<1];
inline void add(int u,int v){
    cnt++;
    a[cnt].to=v;
    a[cnt].nxt=head[u];
    head[u]=cnt;
}
inline pair<int,int> findroot(int rt,int f,int pre){
    sz[rt]=1;
    int mx=0;
    pair<int,int> ans=make_pair(INF,0);
    for(int i=head[rt];i;i=a[i].nxt){
        int t=a[i].to;
        if(vis[t]||t==f) continue;
        ans=min(ans,findroot(t,rt,pre));
        sz[rt]+=sz[t];
        mx=max(mx,sz[t]);
    }
    ans=min(ans,make_pair(max(mx,pre-sz[rt]),rt));
    return ans;
}
struct Dis{
    int f[17][N],dep[N],des[N];
    inline void insert(int x,int fa,int c){
        f[0][x]=fa;
        dep[x]=dep[fa]+1;
        des[x]=des[fa]+c;
        for(int i=1;i<=16;i++){
            f[i][x]=f[i-1][f[i-1][x]];
        }
    }
    inline int LCA(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        for(int i=16;~i;i--){
            if(dep[f[i][x]]>=dep[y]){
                x=f[i][x];
            }
        }
        if(x==y) return x;
        for(int i=16;~i;i--){
            if(f[i][x]!=f[i][y]){
                x=f[i][x],y=f[i][y];
            }
        }
        return f[0][x];
    }
    inline int dis(int x,int y){
        return des[x]+des[y]-2*des[LCA(x,y)];
    }
}bz;
struct Data_Structer{
    vector<int>a,b;
    inline void insert(int x){
        b.insert(lower_bound(b.begin(),b.end(),x),x);
        if(b.size()>=S){
            vector<int>c,d;
            //use 3 not 1 or UB in O2
            int i=0,j=0;
            for(;i<a.size()&&j<b.size();){
                if(a[i]<b[j]){
                    c.push_back(a[i++]);
                }else{
                    c.push_back(b[j++]);
                }
            }
            while(i<a.size()){
                c.push_back(a[i++]);
            }
            while(j<b.size()){
                c.push_back(b[j++]);
            }
            swap(a,c);
            swap(b,d);
        }
    }
    inline int rank(int x){
        return (upper_bound(a.begin(),a.end(),x)-a.begin())+(upper_bound(b.begin(),b.end(),x)-b.begin())-2;
    }
    inline void clear(){
        a.clear(),b.clear();
    }
}pa[N<<1],pb[N<<1];
//smile
//pa:when i is root pb:subtree f_i->i
struct Point_Divide_Tree{
    int f[N],dep[N],siz[N];
    //point x value v in subtree Rt
    //insert -r into DS count dis(j,u)-r_j≤r_i-dis(i,u)
    inline int query(int x,int r){
        int ans=0;
        for(int i=x,j;f[i];i=j){
            j=f[i];
            int dis=r-bz.dis(x,j);
            ans+=pa[j].rank(dis)-pb[i].rank(dis);
        }
        return ans;
    }
    inline void insert(int x,int v,int Rt){
        pa[x].insert(-v);
        for(int i=x,j;i!=Rt;i=j){
            j=f[i];
            int dis=bz.dis(x,j)-v;
            if(j!=Rt) pa[j].insert(dis);
            pb[i].insert(dis);
        }
    }
    //clear "vis" thats dep[i]>deps(rebuild Rt)
    inline void clear(int x,int fa,int deps){
        vis[x]=0;
        for(int i=head[x];i;i=a[i].nxt){
            int t=a[i].to;
            if(t!=fa&&dep[t]>deps){
                clear(t,x,deps);
            }
        }
    }
    //build subtree Rt
    inline int build(int x,int pre,int fa,int Rt){
        int rt=findroot(x,0,pre).second,bas=sz[x];
        pa[rt].clear();
        pb[rt].clear();
        f[rt]=fa;
        siz[rt]=1;
        vis[rt]=1;
        dep[rt]=dep[fa]+1;
        insert(rt,r[rt],Rt);
        for(int i=head[rt];i;i=a[i].nxt){
            int t=a[i].to;
            if(!vis[t]){
                siz[rt]+=build(t,bas,rt,Rt);
            }
        }
        return siz[rt];
    }
    //rebuild subtree x
    inline void rebuild(int x){
        if(!x) return ;
        clear(x,0,dep[x]);
        build(x,siz[x],f[x],f[x]);
    }
    inline void insert(int x,int v){
        pa[x].insert(-v);
        siz[x]++;
        int Rt=0;
        for(int i=x,j;f[i];i=j){
            j=f[i];
            int dis=bz.dis(x,j)-v;
            siz[j]++;
            pa[j].insert(dis);
            pb[i].insert(dis);
            if(siz[i]>siz[j]*alpha){
                Rt=j;
            }
        }
        rebuild(Rt);
    }
}t;
int main(){
    ll last=0;
    read();
    n=read();
    for(int i=1;i<=n;i++){
        vis[i]=1;
        int fa=read()^last%1000000000,c=read();
        r[i]=read();
        bz.insert(i,fa,c);
        if(i>1){
            add(fa,i);
            add(i,fa);
        }
        t.f[i]=fa;
        t.dep[i]=t.dep[fa]+1;
        write(last+=t.query(i,r[i]));
        t.insert(i,r[i]);
        putc('\n');
    }
    flush();
}
/*
Time:52.31s
Memory:162.77MB
*/

再见 qwq~

posted @ 2021-06-11 18:49  ffffyc  阅读(18)  评论(0)    收藏  举报  来源