洛谷P3261 [JLOI2015]城池攻占 【左偏树】

题目传送门

对每个城池维护一个骑士的小根堆,如果堆顶元素小于防御值就弹出,对于还在堆中的元素就在堆顶打上这座城池的贡献标记,合并以及pop的时候下传懒标记。父亲合并儿子的堆即可。

注意long long。
Code:

#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 300005
#define LL long long
using namespace std;
char cb[1<<15],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<15,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
    char c;bool f=0;while(!isdigit(c=getc())) if(c=='-') f=1;
    for(a=c-'0';isdigit(c=getc());a=a*10+c-'0'); if(f) a=-a;
}
#define lc t[x].l
#define rc t[x].r
struct node{
    int l,r,d,id,s,tag;
    LL v,add,mul;
    inline void mdf(int a,LL b,LL c){
        s+=a,tag+=a;
        v=v*c+b;
        mul*=c,add=add*c+b;
    }
}t[maxn];
int n,m,a[maxn],sz,rt[maxn],ans1[maxn],ans2[maxn];
LL h[maxn],V[maxn];
int fir[maxn],nxt[maxn],to[maxn],tot;
inline void line(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
inline void upd(int x){
    if(t[lc].d<t[rc].d) swap(lc,rc);
    t[x].d=t[rc].d+1;
}
inline void pushdown(int x){
    if(lc) t[lc].mdf(t[x].tag,t[x].add,t[x].mul);
    if(rc) t[rc].mdf(t[x].tag,t[x].add,t[x].mul);
    t[x].tag=t[x].add=0,t[x].mul=1;
}
int merge(int x,int y){//
    if(!x||!y) return x+y;
    if(t[x].v>t[y].v) swap(x,y);
    pushdown(x);
    rc=merge(rc,y);
    upd(x);
    return x;
}
int pop(int x){
    ans2[t[x].id]=t[x].s;
    pushdown(x);
    return merge(lc,rc);
}
void dfs(int u){
    for(int i=fir[u],v;i;i=nxt[i]){
        dfs(v=to[i]);
        rt[u]=merge(rt[u],rt[v]);
    }
    while(rt[u]&&t[rt[u]].v<h[u]) ans1[u]++,rt[u]=pop(rt[u]);
    if(rt[u]) t[rt[u]].mdf(1,a[u]?0:V[u],a[u]?V[u]:1);
}
int main()
{
    #ifndef ONLINE_JUDGE
        freopen("H.in","r",stdin);
    #endif
    t[0].d=-1; int x,y;
    read(n),read(m);
    for(int i=1;i<=n;i++) read(h[i]);
    for(int i=2;i<=n;i++) read(x),read(a[i]),read(V[i]),line(x,i);
    for(int i=1;i<=m;i++){
        read(x),read(y);
        t[++sz].id=i,t[sz].v=x,t[sz].mul=1;
        rt[y]=merge(rt[y],sz);
    }
    dfs(1);
    while(rt[1]) rt[1]=pop(rt[1]);
    for(int i=1;i<=n;i++) printf("%d\n",ans1[i]);
    for(int i=1;i<=m;i++) printf("%d\n",ans2[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值