可并堆

Posted Rivendell

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可并堆相关的知识,希望对你有一定的参考价值。

左偏树

bzoj4003 城池攻占

题目大意:一棵树,每个点有一个防御值。m个武士,有攻击力和起始的位置,攻下一个点后会向父亲进攻,攻击力大于等于一个点的防御力就可以攻下,否则死亡。武士攻下每个点后攻击力会变化,加上或者乘上一个数(乘的数保证非负)。问每个城市死亡的武士个数和每个武士攻下的点。

思路:用左偏树维护一个点的武士信息,权值的更改用*a+b的形式更新。

 

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define LL long long
using namespace std;
struct use{int l,r,d;LL ad,ch;}hp[N];
struct uu{int ai;LL vi,hi;}ci[N];
int rt[N]={0},point[N]={0},next[N],cur[N],zh[N],zt,pa[N],ca[N]={0},dis[N]={0};
LL vi[N];
void pushdown(int x){
    int l,r;l=hp[x].l;r=hp[x].r;
    if (l){
        vi[l]=vi[l]*hp[x].ch+hp[x].ad;
        hp[l].ad=hp[l].ad*hp[x].ch+hp[x].ad;
        hp[l].ch=hp[l].ch*hp[x].ch;
    }if (r){
        vi[r]=vi[r]*hp[x].ch+hp[x].ad;
        hp[r].ad=hp[r].ad*hp[x].ch+hp[x].ad;
        hp[r].ch=hp[r].ch*hp[x].ch;
    }hp[x].ad=0LL;hp[x].ch=1LL;
}
int merge(int x,int y){
    if (!x) return y;
    if (!y) return x;
    pushdown(x);pushdown(y);
    if (vi[x]>vi[y]) swap(x,y);
    hp[x].r=merge(hp[x].r,y);
    if (hp[hp[x].l].d<hp[hp[x].r].d) swap(hp[x].l,hp[x].r);
    if (!hp[x].r) hp[x].d=0;
    else hp[x].d=hp[hp[x].r].d+1;
    return x;}
void work(int u){
    while(rt[u]&&vi[rt[u]]<ci[u].hi){
        ++ca[u];pushdown(rt[u]);
        pa[rt[u]]=dis[pa[rt[u]]]-dis[u];
        rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r);
    }if (u!=1){
        if (rt[u]){
            if (ci[u].ai==0){
                vi[rt[u]]+=ci[u].vi;
                hp[rt[u]].ad+=ci[u].vi;
            }else{
                vi[rt[u]]*=ci[u].vi;
                hp[rt[u]].ad*=ci[u].vi;
                hp[rt[u]].ch*=ci[u].vi;
            }
        }
    }else
        while(rt[u]){
            pushdown(rt[u]);
            pa[rt[u]]=dis[pa[rt[u]]]-dis[u]+1;
            rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r);
        }
}
int main(){
    int n,m,i,fi,u,v;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i) scanf("%I64d",&ci[i].hi);
    for (i=2;i<=n;++i){
        scanf("%d%d%I64d",&fi,&ci[i].ai,&ci[i].vi);
        next[i]=point[fi];cur[fi]=point[fi]=i;
    }for (i=1;i<=m;++i){
        scanf("%I64d%d",&vi[i],&fi);
        pa[i]=fi;
        hp[i]=(use){0,0,0,0LL,1LL};
        rt[fi]=merge(rt[fi],i);
    }zh[zt=1]=1;dis[1]=1;
    while(zt){
        u=zh[zt];
        if ((v=cur[u])>0){
            dis[v]=dis[u]+1;
            zh[++zt]=v;
            cur[u]=next[v];
        }else{
            --zt;
            for (v=point[u];v;v=next[v])
                rt[u]=merge(rt[u],rt[v]);
            work(u);
        }
    }for (i=1;i<=n;++i) printf("%d\n",ca[i]);
    for (i=1;i<=m;++i) printf("%d\n",pa[i]);
}
View Code

 

以上是关于可并堆的主要内容,如果未能解决你的问题,请参考以下文章

模板左偏树(可并堆) 可并堆_并查集

可并堆

luogu_P3377 左偏树(可并堆)

[可并堆] Bzoj P1367 sequence

bzoj3011 可并堆

可并堆