城池攻占,题解

题目链接

题意:

  给你一棵树和一些人,每个人从某个节点出发走向跟节点,每个节点有一个权值,每个人有一个权值,人在从某个节点向上走时权值会加或乘上一个数,如果人的权值小于节点的,则不能继续向上,统计每个人可以>节点的数量的个数和在某个节点停下的人。

分析:

  首先先考虑暴力,直接每个节点的人向上跳,跳的时候计算一下能不能继续向上跳,直接统计就好了。

  然后我们会发现,其实向上跳的过程中,好多人是可以合并在一起向上跳的,所以类似归并排序,我们先把人排好序,然后向上跳的时候合并,当然因为如果乘的话保证值为正数,所以加完或乘完之后仍然单调,然后合并两边就好了,这样我们在找最小的时候直接按照顺序跑一遍就完了,但是,这样做还是会被卡掉。

  我们又会发现,其实我们并没有必要记录下所有的顺序,即序列可以是无序的,只要能找到最小,能删除最小后仍能找到最小即可,那么我么自然会想到优先队列,但是优先队列并不支持合并,所以我们要换一个可并堆,这里用的左偏树。

  然后还有一个问题,因为节点的权值要乘或者加,而对于每个儿子,乘或者加的值还不一样,这是我们可以想到线段树的lazy标记,我们在左偏树上加上lazy标记,合并的时候直接下放标记就好了,首先有了标记之后树型并不改变,所以左偏的性质可以保证,又因为乘的都是正的,所以堆的性质也可以保证,然后直接处理就好了。

代码:

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=300000+10;
long long fval[maxn];//点的权值
long long gval[maxn];//人的权值
int fa[maxn];//父亲节点
int ss[maxn];//加还是乘
long long ssval[maxn];//加或乘的值
struct E{
    int to;
    int next;
}ed[maxn];//
int head[maxn];
int tot;
void J(int a,int b){
    tot++;
    ed[tot].to=b;
    ed[tot].next=head[a];
    head[a]=tot;
}
E edr[maxn];//在某个点开始的人
int headr[maxn];
int totr;
void Jr(int a,int b){
    totr++;
    edr[totr].to=b;
    edr[totr].next=headr[a];
    headr[a]=totr;
}
int ha;//节点排到的编号
int de[maxn];//深度
struct TR{//
    int ls;//左孩子
    int rs;//右孩子
    int dis;//距离
    int tot;
    long long lazj;//lazy标记
    long long lazx;
    long long val;//权值
    int x;
    TR(){
        dis=-1;
        val=1e17;
    }
}tr[maxn];
int di[maxn];//在哪个节点停下
int ws[maxn];//这个节点停下来的人
void Dfs(int x,int fa){//处理一下深度
    de[x]=de[fa]+1;
    for(int i=head[x];i;i=ed[i].next)
        Dfs(ed[i].to,x);
}
int yz[maxn];//人从哪个节点开始
int ma[maxn];//某个节点的
void push_down(int x){//下放标记
    int ls=tr[x].ls;
    int rs=tr[x].rs;
    if(ls){//左儿子
        tr[ls].val*=tr[x].lazx;
        tr[ls].val+=tr[x].lazj;
        tr[ls].lazj*=tr[x].lazx;
        tr[ls].lazx*=tr[x].lazx;
        tr[ls].lazj+=tr[x].lazj;
    }
    if(rs){右儿子
        tr[rs].val*=tr[x].lazx;
        tr[rs].val+=tr[x].lazj;
        tr[rs].lazj*=tr[x].lazx;
        tr[rs].lazx*=tr[x].lazx;
        tr[rs].lazj+=tr[x].lazj;
    }
    tr[x].lazx=1;
    tr[x].lazj=0;
}
int Co(int x,int y){
    if(!x||!y)
        return x+y;
    push_down(x);//下放标记
    push_down(y);
    if(tr[x].val>tr[y].val)
        swap(x,y);
    tr[x].rs=Co(tr[x].rs,y);
    if(tr[tr[x].rs].dis>tr[tr[x].ls].dis)
        swap(tr[x].ls,tr[x].rs);
    tr[x].dis=tr[tr[x].rs].dis+1;
    return x;
}
int Pop(int x){
    push_down(x);//下放标记
    return Co(tr[x].ls,tr[x].rs);
}
long long read(){//读入
    long long ans=0;
    long long f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        ans=(ans<<3)+(ans<<1)+ch-'0';
        ch=getchar();
    }
    return f*ans;
}
int Push(int x,int s,long long val){
    ha++;//建立新节点
    tr[ha].ls=0;
    tr[ha].rs=0;
    tr[ha].lazx=1;
    tr[ha].lazj=0;
    tr[ha].val=val;
    tr[ha].x=s;
    tr[ha].dis=0;
    return Co(x,ha);
}
void Dfs_(int x){
    int s=0;
    for(int i=head[x];i;i=ed[i].next){
        Dfs_(ed[i].to);
        s=1;
        if(ss[ed[i].to]==0){//将儿子并入并加标记
            tr[ma[ed[i].to]].lazj+=ssval[ed[i].to];
            tr[ma[ed[i].to]].val+=ssval[ed[i].to];
        }
        else{
            tr[ma[ed[i].to]].lazx*=ssval[ed[i].to];
            tr[ma[ed[i].to]].lazj*=ssval[ed[i].to];
            tr[ma[ed[i].to]].val*=ssval[ed[i].to];
        }
        if(!s&&ma[ed[i].to]){//没有节点之接取下面的编号
            ma[x]=ma[ed[i].to];
            s=1;
        }
        else if(ma[ed[i].to])
            ma[x]=Co(ma[x],ma[ed[i].to]);
    }
    for(int i=headr[x];i;i=edr[i].next){//加入新的人
        if(!s){
            ha++;
            tr[ha].ls=0;
            tr[ha].rs=0;
            tr[ha].lazx=1;
            tr[ha].lazj=0;
            tr[ha].val=gval[edr[i].to];
            tr[ha].x=edr[i].to;
            ma[x]=ha;
            tr[ha].dis=0;
        }
        else
            ma[x]=Push(ma[x],edr[i].to,gval[edr[i].to]);
        s=1;
    }
    if(s==0)
        ma[x]=0;
    while(ma[x]&&tr[ma[x]].val<fval[x]){//处理信息
        ws[x]++;
        di[tr[ma[x]].x]=x;
        ma[x]=Pop(ma[x]);
    }
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);//一些读入
    for(int i=1;i<=n;i++)
        scanf("%lld",&fval[i]);
    for(int i=2;i<=n;i++){
        scanf("%d%d%lld",&fa[i],&ss[i],&ssval[i]);
        J(fa[i],i);
    }
    long long js1;
    int js2;
    for(int i=1;i<=m;i++){
        scanf("%lld%d",&js1,&js2);
        Jr(js2,i);
        gval[i]=js1;
        yz[i]=js2;
    }
    Dfs(1,0);
    Dfs_(1);
    for(int i=1;i<=n;i++)//输出
        printf("%d
",ws[i]);
    for(int i=1;i<=m;i++)
        printf("%d
",de[yz[i]]-de[di[i]]);//简单处理一下
    return 0;
}
原文地址:https://www.cnblogs.com/wish-all-ac/p/12921814.html