BZOJ4003 [JLOI2015]城池攻占

4003: [JLOI2015]城池攻占
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 1266 Solved: 472
[Submit][Status][Discuss]
Description

小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。
这 n 个城池用 1 到 n 的整数表示。除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi < i。也就是说,所有城池构成了一棵有根树。这 m 个骑士用 1 到 m 的整数表示,其中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci。
每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击管辖这座城池的城池,直到占领 1 号城池,或牺牲为止。
除 1 号城池外,每个城池 i 会给出一个战斗力变化参数 ai;vi。若 ai =0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai =1,攻占城池 i 以后,战斗力会乘以 vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

Input
第 1 行包含两个正整数 n;m,表示城池的数量和骑士的数量。
第 2 行包含 n 个整数,其中第 i 个数为 hi,表示城池 i 的防御值。
第 3 到 n +1 行,每行包含三个整数。其中第 i +1 行的三个数为 fi;ai;vi,分别表示管辖这座城池的城池编号和两个战斗力变化参数。
第 n +2 到 n + m +1 行,每行包含两个整数。其中第 n + i 行的两个数为 si;ci,分别表示初始战斗力和第一个攻击的城池。

Output
输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士
数量,后 m 行分别表示骑士 1 到 m 攻占的城池数量。

Sample Input
5 5
50 20 10 10 30
1 1 2
2 0 5
2 0 -10
1 0 10
20 2
10 3
40 4
20 4
35 5

Sample Output
2
2
0
0
0
1
1
3
1
1

HINT
对于 100% 的数据,1 <= n;m <= 300000; 1 <= fi < i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18;ai等于1或者2;当 ai =1 时,vi > 0;保证任何时候骑士战斗力值的绝对值不超过 10^18。


打标记的可并堆。
随机堆有时比左偏树慢,有时快。

左偏树

#include <bits/stdc++.h>
#define N 300010
#define ll long long
#define ld long double
using namespace std;
int n,m,fa[N],a[N],c[N],fir[N],nxt[N<<1],to[N<<1],tot(1),dep[N],ans[N],num[N];
ll h[N],v[N],s[N];
vector<int> p[N];

template <class Aqua>
inline void read(Aqua &s){
    s=0; Aqua f=1; char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) s=s*10+c-'0',c=getchar();
    s*=f;
}

struct heap{
    heap *ch[2],*fa;
    int dep,h,id; ll tag,tag_,x;

    inline void times(ll d){
        x*=d,tag*=d,tag_*=d;
    }

    inline void add(ll d){
        x+=d,tag_+=d;
    }

    inline void pushdown(){
        if (ch[0])
            ch[0]->times(tag),ch[0]->add(tag_);
        if (ch[1])
            ch[1]->times(tag),ch[1]->add(tag_);
        tag=1; tag_=0;
    }

    friend heap *merge(heap *a,heap *b){
        #define height(p) ((p)?p->h:0)
        if (!a) return b;
        if (!b) return a;
        if (a->x>b->x)
            swap(a,b);
        a->pushdown();
        a->ch[1]=merge(a->ch[1],b);
        if (height(a->ch[1])>height(a->ch[0]))
            swap(a->ch[0],a->ch[1]);
        a->h=height(a->ch[1])+1;
        return a;
    }

    heap *pop(){
        this->pushdown();
        return merge(ch[0],ch[1]);
    }
}pool[N],*tail=pool;

heap *newnode(ll x,int d){
    tail->x=s[x],tail->id=x,tail->dep=d;
    tail->h=tail->tag=1; tail->tag_=0;
    tail->ch[0]=tail->ch[1]=0;
    return tail++;
}

inline void add(int u,int v){
    to[++tot]=v; nxt[tot]=fir[u]; fir[u]=tot;
}

heap *dfs(int x){
    dep[x]=dep[fa[x]]+1;
    heap *cur=0;
    for (int i=0;i<p[x].size();i++)
        cur=merge(cur,newnode(p[x][i],dep[x]));
    for (int i=fir[x];i;i=nxt[i])
        if (to[i]!=fa[x])
            cur=merge(cur,dfs(to[i]));
    for (;cur && cur->x<h[x];cur=cur->pop()){
        cur->pushdown();
        num[cur->id]=cur->dep-dep[x];
        ans[x]++;
    }
    if (cur)
        (a[x]?cur->times(v[x]):cur->add(v[x]));
    return cur;
}

int main(){
    read(n),read(m);
    for (int i=1;i<=n;i++)
        read(h[i]);
    for (int i=2;i<=n;i++){
        read(fa[i]),read(a[i]),read(v[i]);
        add(i,fa[i]),add(fa[i],i);
    }
    for (int i=1;i<=m;i++){
        read(s[i]),read(c[i]);
        p[c[i]].push_back(i);
    }
    heap *cur=dfs(1);
    for (;cur;cur=cur->pop()){
        cur->pushdown();
        num[cur->id]=cur->dep;
    }
    for (int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    for (int i=1;i<=m;i++)
        printf("%d\n",num[i]);
    return 0;
}

随机堆

#include <bits/stdc++.h>
#define N 300010
#define ll long long
#define ld long double
using namespace std;
int n,m,fa[N],a[N],c[N],fir[N],nxt[N<<1],to[N<<1],tot(1),dep[N],ans[N],num[N];
ll h[N],v[N],s[N];
vector<int> p[N];

template <class Aqua>
inline void read(Aqua &s){
    s=0; Aqua f=1; char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
    while (isdigit(c)) s=s*10+c-'0',c=getchar();
    s*=f;
}

struct heap{
    heap *ch[2],*fa;
    int dep,id; ll tag,tag_,x;

    inline void times(ll d){
        x*=d,tag*=d,tag_*=d;
    }

    inline void add(ll d){
        x+=d,tag_+=d;
    }

    inline void pushdown(){
        if (ch[0])
            ch[0]->times(tag),ch[0]->add(tag_);
        if (ch[1])
            ch[1]->times(tag),ch[1]->add(tag_);
        tag=1; tag_=0;
    }

    friend heap *merge(heap *a,heap *b){
        if (!a) return b;
        if (!b) return a;
        if (a->x>b->x)
            swap(a,b);
        a->pushdown();
        int x=rand()&1;
        a->ch[x]=merge(a->ch[x],b);
        return a;
    }

    heap *pop(){
        this->pushdown();
        return merge(ch[0],ch[1]);
    }
}pool[N],*tail=pool;

heap *newnode(ll x,int d){
    tail->x=s[x],tail->id=x,tail->dep=d;
    tail->tag=1; tail->tag_=0;
    tail->ch[0]=tail->ch[1]=0;
    return tail++;
}

inline void add(int u,int v){
    to[++tot]=v; nxt[tot]=fir[u]; fir[u]=tot;
}

heap *dfs(int x){
    dep[x]=dep[fa[x]]+1;
    heap *cur=0;
    for (int i=0;i<p[x].size();i++)
        cur=merge(cur,newnode(p[x][i],dep[x]));
    for (int i=fir[x];i;i=nxt[i])
        if (to[i]!=fa[x])
            cur=merge(cur,dfs(to[i]));
    for (;cur && cur->x<h[x];cur=cur->pop()){
        cur->pushdown();
        num[cur->id]=cur->dep-dep[x];
        ans[x]++;
    }
    if (cur)
        (a[x]?cur->times(v[x]):cur->add(v[x]));
    return cur;
}

int main(){
    srand(time(NULL));
    read(n),read(m);
    for (int i=1;i<=n;i++)
        read(h[i]);
    for (int i=2;i<=n;i++){
        read(fa[i]),read(a[i]),read(v[i]);
        add(i,fa[i]),add(fa[i],i);
    }
    for (int i=1;i<=m;i++){
        read(s[i]),read(c[i]);
        p[c[i]].push_back(i);
    }
    heap *cur=dfs(1);
    for (;cur;cur=cur->pop()){
        cur->pushdown();
        num[cur->id]=cur->dep;
    }
    for (int i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    for (int i=1;i<=m;i++)
        printf("%d\n",num[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值