zoukankan      html  css  js  c++  java
  • 城池攻占,题解

    题目链接

    题意:

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

    分析:

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

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

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

      然后还有一个问题,因为节点的权值要乘或者加,而对于每个儿子,乘或者加的值还不一样,这是我们可以想到线段树的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;
    }
  • 相关阅读:
    Anaconda和Miniconda的安装
    并联谐振电路频率阻抗及计算曲线带宽案例摘要
    TEM波
    Android Studio实现简单考试应用程序
    android studio实现简单考试应用程序
    Android Studio的简易计算器界面设计
    时谐变换
    简要理解什么是趋肤效应
    c盘清理
    如何把Visual Studio完全安装在其他磁盘
  • 原文地址:https://www.cnblogs.com/wish-all-ac/p/12921814.html
Copyright © 2011-2022 走看看