其他分享
首页 > 其他分享> > 题目分享D 三代目

题目分享D 三代目

作者:互联网

题意:

小铭铭最近获得了一副新的桌游,游戏中需要用 mm 个骑士攻占 nn 个城池。

这 nn 个城池用 11 到 nn 的整数表示。除 11 号城池外,城池 ii 会受到另一座城池 fifi 的管辖,其中 fi<ifi<i。也就是说,所有城池构成了一棵有根树。这 mm 个骑士用 11 到 mm 的整数表示,其中第 ii 个骑士的初始战斗力为 sisi,第一个攻击的城池为 cici。

每个城池有一个防御值 hihi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击管辖这座城池的城池,直到占领 11 号城池,或牺牲为止。

除 11 号城池外,每个城池 ii 会给出两个战斗力变化参数 ai,viai,vi。若 ai=0ai=0,攻占城池 ii 以后骑士战斗力会增加 vivi;若 ai=1ai=1,攻占城池 ii 以后,战斗力会乘以 vivi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。

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

分析:

一开始想写倍增,结果发现数组开不开。

再重新考虑暴力的优化

一个一个人跳的话显然会同时走过很多重复的路径

所以可以用类似树上差分的思路,

从最底下的人开始往上跳,顺便带着上面的人一起走

那么就可以给每个点开一个小根堆记录当前点能活着的人

那么用小根堆显然是比较方便把失败的人弹出去

而且用左偏树还可以进行子节点向上合并的合并操作

但问题是每次往上跳

该小根堆的每个节点都要进行乘或加的操作也就是会被修改

而每次遍历所有节点修改其值显然还是会t

所以就要用类似线段树的方法打上乘和加的标记

如:打乘的标记:

 

 加的标记:

 

 记得要先乘后加

 然后merge部分与板子类似,不过选出当前根节点之前要先把标记下传,要不然标记会影响到新并进来的(显然)

 

 因为题目中fi<i的,所以其实从底开始枚举时只需要从n->1枚举即可,求deep值时也只需要从1->n 遍历一遍即可,不需要dfs

 如果有人一路打到通关,那我们就把它挂在0号节点上,没通关的人结果提前就能求出来

通关并挂在0节点上的人结果就是deep值,因为显然他通关了一定打过deep个城池

代码:

#include<cstdio>
#include<algorithm>
using namespace std;

#define ll long long

const int maxn=3e5+1;

int fa[maxn];
int c[maxn];
int ansn[maxn];
int ansm[maxn];
int deep[maxn];
int root[maxn];
int dis[maxn];
int ls[maxn];
int rs[maxn];
ll h[maxn];
ll mul[maxn];
ll add[maxn];
ll s[maxn];
ll tag_mul[maxn];
ll tag_add[maxn];

void q_add(int x,ll y)
{
    s[x]+=y,tag_add[x]+=y;
}

void q_mul(int x,ll y)
{
    s[x]*=y,tag_add[x]*=y,tag_mul[x]*=y;
}

void down(int x)
{
    if(tag_mul[x]!=1)
    {
        if(ls[x]) q_mul(ls[x],tag_mul[x]);
        if(rs[x]) q_mul(rs[x],tag_mul[x]);
        tag_mul[x]=1;
    }
    if(tag_add[x])
    {
        if(ls[x]) q_add(ls[x],tag_add[x]);
        if(rs[x]) q_add(rs[x],tag_add[x]);
        tag_add[x]=0;
    }
}

int merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(s[x]>s[y]) swap(x,y);
    down(x),rs[x]=merge(rs[x],y);
    if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
    dis[x]=dis[rs[x]]+1;
    return x;
}

void dfs(int x)
{
    ansm[x]=deep[c[x]];
    if(ls[x]) dfs(ls[x]);
    if(rs[x]) dfs(rs[x]);
}

int main()
{
    int n,m,p;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
    for(int i=2;i<=n;i++)
    {
        scanf("%d%d",&fa[i],&p);
        if(p) scanf("%lld",&mul[i]),add[i]=0;
        else scanf("%lld",&add[i]),mul[i]=1;
    }
    for(int i=1;i<=n;i++) deep[i]=deep[fa[i]]+1;
    for(int i=1;i<=m;i++) tag_mul[i]=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%d",&s[i],&c[i]);
        root[c[i]]=merge(root[c[i]],i);
    }
    for(int i=n;i>=1;i--)
    {
        while(root[i]&&s[root[i]]<h[i])
        {
            ansm[root[i]]=deep[c[root[i]]]-deep[i];
            down(root[i]);ansn[i]++;
            root[i]=merge(ls[root[i]],rs[root[i]]);
        }
        if(root[i])
        {
            if(add[i]) q_add(root[i],add[i]);
            if(mul[i]!=1) q_mul(root[i],mul[i]);
            root[fa[i]]=merge(root[fa[i]],root[i]);
        }
    }
    dfs(root[0]);
    for(int i=1;i<=n;i++) printf("%d\n",ansn[i]);
    for(int i=1;i<=m;i++) printf("%d\n",ansm[i]);
    return 0;
}

标签:题目,int,城池,add,tag,maxn,mul,三代,分享
来源: https://www.cnblogs.com/lin4xu/p/12930736.html