[BZOJ4003][JLOI2015][可并堆]城池攻占

对于每个点维护一颗左偏树,最开始这棵树只包含从这个点出发的人。
可以按拓扑序枚举,枚举到一个点,把它的左偏树和所有儿子的左偏树合并(因为是左偏树,所以合并复杂度是logn),把战斗力小的弹出。

至于修改打两个标记就好了。

#include <cstdio>
#define N 300010

typedef long long ll;

struct tree{
    ll taga,tagb,w;
    int d,l,r,g;
}T[N];

struct edge{
    int t,nx;
}E[N<<1];

int n,m,u,x,cnt,t;
int G[N],dsl[N],dpt[N],Root[N],f[N];
ll A[N],B[N],C[N],Ans1[N],Ans2[N];

inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    if(p1==p2){
        p2=(p1=buf)+fread(buf,1,100000,stdin);
        if(p1==p2) return EOF;
    }
    return *p1++;
}

inline void reaD(int &x){
    char Ch=gc();x=0;int f=1;
    for(;Ch>'9'||Ch<'0';Ch=gc())if(Ch=='-')f=-1;
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=gc());x*=f;
}

inline void reaD(ll &x){
    char Ch=gc();x=0;int f=1;
    for(;Ch>'9'||Ch<'0';Ch=gc())if(Ch=='-')f=-1;
    for(;Ch>='0'&&Ch<='9';x=x*10+Ch-'0',Ch=gc());x*=f;
}

inline void InserT(int x,int y){
    E[++cnt].t=y;E[cnt].nx=G[x];G[x]=cnt;
}

inline int newd(int x,int g){
    T[++cnt].w=x;T[cnt].g=g;
    T[cnt].taga=1;T[cnt].tagb=0;
    return cnt;
}

inline void swap(int &x,int &y){
    int z=x;x=y;y=z;
}

inline void liflag(int x,ll a,ll b){
    if(a) T[x].taga*=a,T[x].tagb*=a;
    if(b) T[x].tagb+=b;
}

inline void pushdown(int x){
    if(T[x].taga>1){
        T[x].w*=T[x].taga;
        liflag(T[x].l,T[x].taga,0);
        liflag(T[x].r,T[x].taga,0);
        T[x].taga=1;
    }
    if(T[x].tagb){
        T[x].w+=T[x].tagb;
        liflag(T[x].l,0,T[x].tagb);
        liflag(T[x].r,0,T[x].tagb);
        T[x].tagb=0;
    }
}

int merge(int x,int y){
    if(x==0||y==0) return x+y;
    pushdown(x);pushdown(y);
    if(T[x].w>T[y].w) swap(x,y);
    T[x].r=merge(T[x].r,y);
    if(T[T[x].r].d>T[T[x].l].d) swap(T[x].l,T[x].r);
    if(T[x].r==0) T[x].d=0; else T[x].d=T[T[x].r].d+1;
    return x;
}

void dfs(int x){
    dsl[++t]=x;
    for(int i=G[x];i;i=E[i].nx){
        dpt[E[i].t]=dpt[x]+1;
        dfs(E[i].t);
    }
}

inline ll top(int x){
    pushdown(x);
    return T[x].w;
}

inline int pop(int x){
    pushdown(x);
    return merge(T[x].l,T[x].r);
}

int w[30],wt;

inline void Pt(ll x){
    if(!x){putchar(48);putchar('\n');return;}
    while(x)w[++wt]=x%10,x/=10;
    for(;wt;wt--)putchar(48+w[wt]);putchar('\n');
}

int main(){
    #ifndef ONLINE_JUDGE
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    #endif
    reaD(n);reaD(m);
    for(int i=1;i<=n;i++) reaD(A[i]);
    for(int i=2;i<=n;i++) reaD(u),reaD(B[i]),reaD(C[i]),InserT(u,i);
    cnt=0;
    for(int i=1;i<=m;i++){
        reaD(x);reaD(f[i]);
        Root[f[i]]=merge(Root[f[i]],newd(x,i));
    }
    dfs(1);
    for(int k=t;k;k--){
        int x=dsl[k];
        for(int i=G[x];i;i=E[i].nx) Root[x]=merge(Root[x],Root[E[i].t]);
        while(Root[x]!=0&&top(Root[x])<A[x]){
            Ans2[T[Root[x]].g]=dpt[f[T[Root[x]].g]]-dpt[x];
            Root[x]=pop(Root[x]),Ans1[x]++;
        }
        if(B[x]) liflag(Root[x],C[x],0);
        else liflag(Root[x],0,C[x]);
    }
    while(Root[1]!=0){
        Ans2[T[Root[1]].g]=dpt[f[T[Root[1]].g]]+1;
        Root[1]=pop(Root[1]);
    }
    for(int i=1;i<=n;i++) Pt(Ans1[i]);
    for(int i=1;i<=m;i++) Pt(Ans2[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值