[BZOJ3435][Wc2014]紫荆花之恋-替罪羊树-treap-动态点分治

本文介绍了一个关于在动态变化的树结构中计算满足特定距离条件的朋友对数量的问题。通过巧妙地利用数据结构如平衡树(treap)和分治策略,实现了高效的算法设计。文章详细探讨了算法实现细节,包括如何处理节点添加及树的重构等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

紫荆花之恋

Description

强强和萌萌是一对好朋友。有一天他们在外面闲逛,突然看到前方有一棵紫荆树。这已经是紫荆花飞舞的季节了,无数的花瓣以肉眼可见的速度从紫荆树上长了出来。仔细看看的话,这个大树实际上是一个带权树。每个时刻它会长出一个新的叶子节点。每个节点上有一个可爱的小精灵,新长出的节点上也会同时出现一个新的小精灵。小精灵是很萌但是也很脆弱的生物,每个小精灵 i 都有一个感受能力值Ri ,小精灵 i, j 成为朋友当且仅当在树上 i 和 j 的距离 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在这个树上从 i 到 j 的唯一路径上所有边的边权和。强强和萌萌很好奇每次新长出一个叶子节点之后,这个树上总共有几对朋友。
我们假定这个树一开始为空,节点按照加入的顺序从 1开始编号。由于强强非常好奇, 你必须在他每次出现新节点后马上给出总共的朋友对数,不能拖延哦。

Input

共有 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。

Output

包含 n 行,每行输出1 个整数, 表示加入第 i 个点之后,树上有几对朋友。

Sample Input

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4

Sample Output

0
1
2
4
7

HINT

1<=Ci<=10000
Ai<=2*10^9
Ri<=10^9
N<=100000


调了一天+一个上午,一半以上时间在卡常……
唉咱为什么会来写这鬼东西……


思路:
考虑化简式子。

dis(i,j)ri+rjdis(i,lca)+dis(lca,j)ri+rjdis(i,lca)rirjdis(lca,j)dis(i,j)≤ri+rjdis(i,lca)+dis(lca,j)≤ri+rjdis(i,lca)−ri≤rj−dis(lca,j)

于是任务目标可以化成,维护一棵树,支持加一个叶子节点jj,并同时查询树上满足dis(i,lca)rirjdis(lca,j)的点的个数。

如果只是单纯的统计,而没有添加叶子结点,那么这是一个经典的动态点分治套路题:
(咱居然能很淡定的说动态点分治是套路诶)
对于每个分治中心pp,用一棵平衡树维护分治子树内所有点idis(i,p)ridis(i,p)−ri的值。
此时如果lca(i,j)==plca(i,j)==p,那么有平衡树中满足此条件的所有权值比rjdis(j,p)rj−dis(j,p)的点与ii点可以组成符合条件的点对。

考虑到不一定满足lca(i,j)==p的限制,对每个分治中心pp再开一棵平衡树记录分治子树内的所有点idis(i,fa(p))ridis(i,fa(p))−ri的值。
在询问时,通过减去这棵树上的查询结果容斥掉这部分答案。

考虑支持可以添加叶子结点的情况。
分治中心一定是重心吗?
显然不需要,令重心为分治中心只是为了保证复杂度。
因此咱们可以考虑把每个新插入的节点直接当做一个只包含它自己的分治子树,它在点分树上的父亲为它在原树中的直接父亲,同样建出平衡树并一路向上查询和更新。

当然这样复杂度是错的。一旦遇到链之类的东西就会光速爆炸。
考虑优化,引入替罪羊思想,若一个节点过于不平衡,便拍扁重建。
重建将使用点分治算法,以保证重建出来的分治结构尽量平衡,从而保证复杂度。

平衡树这里选择了treap。复杂度O(nlog2n)O(nlog2n)
咱常数太大,被关了起来…..

以下代码超长,请小心食用。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef set<int>::iterator sint;
const int N=100009;
const int M=N*60;
const int K=17;
int open=0;
ll lans;

#ifdef __linux__
#define getchar getchar_unlocked
#endif

namespace istreams
{
    #define L (1<<16)
    char buffer[N],*S,*T;
    char getchars()
    {
        if(S==T)
        {
            T=(S=buffer)+fread(buffer,1,L,stdin);
            if(S==T)return EOF;
        }
        return *S++;
    }
    int read()
    {
        int x=0;char ch=getchars();
        while(ch<'0'|| ch>'9')ch=getchars();
        while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchars();
        return x;
    }
}

using namespace istreams;

inline void write(ll x)
{
    if(x>=10)write(x/10);
    putchar('0'+x%10);
}

inline void chkmax(int &a,int b){if(a<b)a=b;}

int n;

namespace treap
{
    int ch[M][2],fa[M],key[M];
    int val[M],cnt[M],siz[M];
    int stk[M],top;

    inline int urand()
    {
    #ifndef __linux__
        return rand()<<16|rand();
    #endif
        return rand();
    }

    inline void init()
    {
        srand(514);
        top=M-2;
        for(int i=1;i<=top;i++)
            stk[i]=top-i+1;
    }

    inline int newnode(int v=0)
    {
        int ret=stk[top--];
        key[ret]=urand();cnt[ret]=1;
        val[ret]=v;siz[ret]=1;
        return ret;
    }

    inline void update(int x)
    {
        siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+cnt[x];
    }

    inline void rotate(int x)
    {
        int y=fa[x],z=fa[y];
        int l=(ch[y][1]==x);
        if(z)ch[z][ch[z][1]==y]=x;
        fa[x]=z;fa[y]=x;fa[ch[x][l^1]]=y;
        ch[y][l]=ch[x][l^1];ch[x][l^1]=y;
        update(y);update(x);
    }

    inline void insert(int &now,int v,int fat)
    {
        if(!now){now=newnode(v);fa[now]=fat;return;}
        int son;
        if(v<val[now])
            insert(ch[now][son=0],v,now);
        else if(v==val[now])
            cnt[now]++;
        else
            insert(ch[now][son=1],v,now);
        update(now);
        if(v!=val[now] && fat && key[ch[now][son]]>key[now])
            rotate(ch[now][son]);
    }

    inline int query(int x,int v)
    {
        int ans=0;
        while(x)
        {
            if(v<val[x])
                x=ch[x][0];
            else if(v==val[x])
                return ans+siz[ch[x][0]]+cnt[x];
            else
            {
                ans+=siz[ch[x][0]]+cnt[x];
                x=ch[x][1];
            }
        }
        return ans;
    }

    inline void free(int &x)
    {
        if(!x)return;
        stk[++top]=x;
        free(ch[x][0]);
        free(ch[x][1]);
        val[x]=cnt[x]=0;x=0;
    }
}

namespace origin
{
    int fa[K][N],dep[N],dis[N];

    inline void add(int u,int v,int c)
    {
        fa[0][v]=u;
        dep[v]=dep[u]+1;
        dis[v]=dis[u]+c;
        for(int i=1;i<K;i++)
            fa[i][v]=fa[i-1][fa[i-1][v]];
    }

    inline int lca(int a,int b)
    {
        if(dep[b]<dep[a])swap(a,b);
        int dlt=dep[b]-dep[a];
        for(int i=K-1,j=(1<<K-1);i>=0;i--,j>>=1)
            if(dlt&j)
                b=fa[i][b];
        if(a==b)return a;
        for(int i=K-1;i>=0;i--)
            if(fa[i][a]!=fa[i][b])
                a=fa[i][a],b=fa[i][b];
        return fa[0][a];
    }

    inline int dist(int u,int v)
    {
        return dis[u]+dis[v]-(dis[lca(u,v)]<<1);
    }
}

namespace tdc
{
    const double alpha=0.88;
    int eto[N<<1],enxt[N<<1],ew[N<<1],ebeg[N],tot=1;
    int siz[N],vis[N],tfa[N],tsz[N],tmx[N],mem,tim;
    int r[N],fa[N],rts[N],rtfa[N],q[N],qr;
    set<int> to[N];
    int ban[N<<1];

    inline void add(int u,int v,int c)
    {
        eto[++tot]=v;
        enxt[tot]=ebeg[u];
        ew[tot]=c;
        ebeg[u]=tot;
    }

    inline int getsz(int u)
    {
        q[qr=1]=u;tfa[u]=0;
        for(int l=1,u=q[l];l<=qr;u=q[++l])
        {
            tmx[u]=tsz[u]=1;
            for(int i=ebeg[u];i;i=enxt[i])
                if(vis[eto[i]]==tim && ban[i]!=tim && eto[i]!=tfa[u])
                    tfa[eto[i]]=u,q[++qr]=eto[i];
        }
        return qr;
    }

    inline void getrt(int totsz,int &rt)
    {
        for(int i=qr,u=q[i];i>=1;u=q[--i])
        {
            chkmax(tmx[u],totsz-tsz[u]);
            if((tmx[u]<<1)<=totsz){rt=u;return;}
            if(tfa[u])
            {
                chkmax(tmx[tfa[u]],tsz[u]);
                tsz[tfa[u]]+=tsz[u];
            }
        }
    }

    inline void insert_t(int u,int fat,int dep,int &rt)
    {
        treap::insert(rt,dep-r[u],0);
        for(int i=ebeg[u];i;i=enxt[i])
            if(vis[eto[i]]==tim && ban[i]!=tim && eto[i]!=fat)
                insert_t(eto[i],u,dep+ew[i],rt);
    }

    inline int btdc(int u)
    {
        int totsiz=getsz(u),rt=u;
        getrt(totsiz,rt);
        insert_t(rt,0,0,rts[rt]);
        to[rt].clear();
        siz[rt]=totsiz;

        for(int i=ebeg[rt],v;i;i=enxt[i])
            if(ban[i]!=tim && vis[eto[i]]==tim)
            {
                ban[i]=ban[i^1]=tim;

                int tmp=0;
                insert_t(eto[i],0,ew[i],tmp);
                v=btdc(eto[i]);
                rtfa[v]=tmp;
                fa[v]=rt;
                to[rt].insert(v);
            }

        return rt;
    }

    inline void del(int u)
    {
        vis[u]=tim;
        treap::free(rts[u]);
        for(sint i=to[u].begin();i!=to[u].end();i++)
        {
            treap::free(rtfa[(*i)]);
            del(*i);
        }
    }

    inline void rebuild(int u)
    {
        int fat=fa[u],mrt=rtfa[u];
        if(fat)
            to[fat].erase(u);
        tim++;del(u);
        int tmp=btdc(u);
        fa[tmp]=fat;
        if(fat)
            to[fat].insert(tmp),rtfa[tmp]=mrt;
    }

    inline void insert(int u,int fat,int rv,int c)
    {
        mem=0;fa[u]=fat;
        siz[u]=1;r[u]=rv;
        to[fat].insert(u);
        for(int i=u,d;i;i=fa[i])
        {
            if(fa[i])
            {
                siz[fa[i]]++;d=origin::dist(u,fa[i]);
                lans+=treap::query(rts[fa[i]],r[u]-d);
                lans-=treap::query(rtfa[i],r[u]-d);
                treap::insert(rtfa[i],d-r[u],0);
                if(siz[i]>alpha*siz[fa[i]])
                    mem=fa[i];
            }
            treap::insert(rts[i],origin::dist(u,i)-r[u],0);
        }   
        if(mem)
            rebuild(mem);
    }
}

int main()
{
    treap::init();
    read();n=read();
    for(int i=1,a,c,r;i<=n;i++)
    {
        a=read();c=read();r=read();
        if(!open)a^=lans%1000000000;
        origin::add(a,i,c);
        if(a)
        {
            tdc::add(a,i,c);
            tdc::add(i,a,c);
        }
        tdc::insert(i,a,r,c);
        write(lans);putchar('\n');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值