[JLOI2015]城池攻占

[JLOI2015]城池攻占

题目描述

  小铭铭最近获得了一副新的桌游,游戏中需要用 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。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。

  现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

输入格式:

  第 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,分别表示初始战斗力和第一个攻击的城池。 

输出格式:

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

 

输入样例: 

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

输出样例:

2
2
0
0
0
1
1
3
1
1

说明

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

思路

  我们看一下题目,题目之中说,如果当前骑士如果攻占了当前的城池,他就会继续前往下一个城池,那么这样我们就可以选择一个可以进行合并的数据结构。继续读题,我们发现如果当前骑士的能力值小于当前的城池,他就会被击杀,这样我们想到可以运用小根堆,这样我们就可以快速杀死所有能力小于城池的骑士。两者结合起来就是可并堆。然而我们依旧需要继续读题,题目之中说了,我们每一次到一个城池,能打下这个城池的所有骑士的能力值都会对应有所改变,如果我们每一次都暴力修改,是不是太慢了?因为修改程度是一样的,所以堆的内部结构不会改变,这样的话,我们可以用一个数组,类似于lazy标记,表示以当前节点为根的堆所有点的变化量,当然这个数组应该由两部分组成,一部分来存乘的数值,另一部分存加的数值。修改就好了。注意先乘后加!!!

代码

  手写栈的写法(有的oj不开全栈)
// luogu-judger-enable-o2
#include <stdio.h>
#include <algorithm>
using namespace std;
int order[300001];
int lson[300001];
int rson[300001];
int dis[300001];
long long need[300001];
long long num[300001];
int head[300001];
int to[300001];
int nxt[300001];
long long v[300001];
int fro[300001];
int level[300001];
int ans[300001];
int ans2[300001];
bool is[300001];
long long oper1[300001];
long long oper2[300001];
int n,m,idx;
int str[300001];
int from[300001];
void add(int x,int y)
{
    nxt[++idx]=head[x];
    head[x]=idx;
    to[idx]=y;
}
void change(int p)
{
    num[p]*=oper1[p];
    num[p]+=oper2[p];
    oper1[p]=1;
    oper2[p]=0;
}
void pushdown(int p)
{
    if(lson[p])
    {
        oper1[lson[p]]*=oper1[p];
        oper2[lson[p]]*=oper1[p];
        oper2[lson[p]]+=oper2[p];
    }
    if(rson[p])
    {
        oper1[rson[p]]*=oper1[p];
        oper2[rson[p]]*=oper1[p];
        oper2[rson[p]]+=oper2[p];
    }
    change(p);
}
int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    pushdown(x);pushdown(y);
    if(num[y]<num[x]) swap(x,y);
    rson[x]=merge(rson[x],y);
    if(dis[rson[x]]>dis[lson[x]])
        swap(lson[x],rson[x]);
    dis[x]=dis[rson[x]]+1;
    return x;
}
int check(int pla,int p)
{
    pushdown(p);
    while(need[pla]>num[p]&&p)
    {
        ans[p]=level[fro[p]]-level[pla];
        p=merge(lson[p],rson[p]);
        ans2[pla]++;
        pushdown(p);
    }
    return p;
}
void dfs(int p)
{
    int l,hed;
    l=idx=0,str[++idx]=p,level[1]=1;
    while(l<idx)
    {
        p=str[++l];
        for(int i=head[p];i;i=nxt[i])
            str[++idx]=to[i],from[idx]=p,level[to[i]]=level[p]+1;
    }
    while(idx)
    {
        p=str[idx];
        order[p]=check(p,order[p]);
        if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p];
        if(!is[p]) oper2[order[p]]+=v[p];
        order[from[idx]]=merge(order[from[idx]],order[p]);
        idx--;
    }/*
    int tmp;
    level[p]=level[from]+1;
    for(int i=head[p];i;i=nxt[i])
        tmp=dfs(to[i],p),order[p]=merge(order[p],tmp);
    order[p]=check(p,order[p]);
    if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p];
    if(!is[p]) oper2[order[p]]+=v[p];
    return order[p];*/
}
int main()
{
    dis[0]=-1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&need[i]);
    for(int i=2;i<=n;i++)
    {
        int a,b;
        scanf("%d%d%lld",&a,&b,&v[i]);
        add(a,i);
        if(b==1) is[i]=true;
    }
    for(int i=1;i<=m;i++)
    {
        int b;
        oper1[i]=1;
        scanf("%lld%d",&num[i],&b);
        order[b]=merge(order[b],i);
        fro[i]=b;
    }
    dfs(1);
    need[0]=((long long)1e18+1);
    check(0,order[1]);
    for(int i=1;i<=n;i++)
        printf("%d
",ans2[i]);
    for(int i=1;i<=n;i++)
        printf("%d
",ans[i]);
}

  运用系统栈的写法(有的oj开全栈)

#include <stdio.h>
#include <algorithm>
using namespace std;
int order[300001];
int lson[300001];
int rson[300001];
int dis[300001];
long long need[300001];
long long num[300001];
int head[300001];
int to[300001];
int nxt[300001];
long long v[300001];
int fro[300001];
int level[300001];
int ans[300001];
int ans2[300001];
bool is[300001];
long long oper1[300001];
long long oper2[300001];
int n,m,idx;
int str[300001];
int from[300001];
void add(int x,int y)
{
    nxt[++idx]=head[x];
    head[x]=idx;
    to[idx]=y;
}
void change(int p)
{
    num[p]*=oper1[p];
    num[p]+=oper2[p];
    oper1[p]=1;
    oper2[p]=0;
}
void pushdown(int p)
{
    if(lson[p])
    {
        oper1[lson[p]]*=oper1[p];
        oper2[lson[p]]*=oper1[p];
        oper2[lson[p]]+=oper2[p];
    }
    if(rson[p])
    {
        oper1[rson[p]]*=oper1[p];
        oper2[rson[p]]*=oper1[p];
        oper2[rson[p]]+=oper2[p];
    }
    change(p);
}
int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    pushdown(x);pushdown(y);
    if(num[y]<num[x]) swap(x,y);
    rson[x]=merge(rson[x],y);
    if(dis[rson[x]]>dis[lson[x]])
        swap(lson[x],rson[x]);
    dis[x]=dis[rson[x]]+1;
    return x;
}
int check(int pla,int p)
{
    pushdown(p);
    while(need[pla]>num[p]&&p)
    {
        ans[p]=level[fro[p]]-level[pla];
        p=merge(lson[p],rson[p]);
        ans2[pla]++;
        pushdown(p);
    }
    return p;
}
int dfs(int p,int from)
{/*
    int l,hed;
    l=idx=0;
    str[++idx]=p;
    while(l<idx)
    {
        p=str[++l];
        for(int i=head[p];i;=nxt[i])
            str[++idx]=p,from[idx]=p;
    }
    while(idx)
    {
        p=str[idx--];
        hed=check(p,order[p]);
        if(is[p]) oper1[hed]*=v[p],oper2[hed]*=v[p];
        if(!is[p]) oper2[hed]+=v[p];
        order[p]=merge()
    }*/
    int tmp;
    level[p]=level[from]+1;
    for(int i=head[p];i;i=nxt[i])
        tmp=dfs(to[i],p),order[p]=merge(order[p],tmp);
    order[p]=check(p,order[p]);
    if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p];
    if(!is[p]) oper2[order[p]]+=v[p];
    return order[p];
}
int main()
{
    dis[0]=-1;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&need[i]);
    for(int i=2;i<=n;i++)
    {
        int a,b;
        scanf("%d%d%lld",&a,&b,&v[i]);
        add(a,i);
        if(b==1) is[i]=true;
    }
    for(int i=1;i<=m;i++)
    {
        int b;
        oper1[i]=1;
        scanf("%lld%d",&num[i],&b);
        order[b]=merge(order[b],i);
        fro[i]=b;
    }
    int hed=dfs(1,0);
    need[0]=((long long)1e18+1);
    check(0,hed);
    for(int i=1;i<=n;i++)
        printf("%d
",ans2[i]);
    for(int i=1;i<=n;i++)
        printf("%d
",ans[i]);
}
原文地址:https://www.cnblogs.com/yangsongyi/p/9046913.html