BZOJ4003:[JLOI2015]城池攻占——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=4003

https://www.luogu.org/problemnew/show/P3261

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

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

讲真,这题出在省选里就是为了卡掉一群不学左偏树的人。

题还真不难,左偏树比较裸的题。

暴力显然是O(nm)的。

但是如果我们对于每次模拟爬树的时候多让一些人跟着一起爬的话就能优化时间。

于是我们从叶子节点开始往根爬,把沿途节点的人合并到一起,维护一下这些人战斗力最小的那个,这样碰到城池打不破的时候弹掉最小的,直到最小的那个能打过了就是剩下的人都能打过了。

(当然说的那么简单你需要至少push两个lazy标记来更新战斗力,所以真恶心。)

分析复杂度(仅供参考因为我不会分析),最坏到最后一个人都没死,合并复杂度O(mlogm),弹出也是O(mlogm),只爬了一次树所以是O(n),于是整体复杂度就是O(mlogm)的了。

(当然这题你需要卡常技巧,洛谷一个点1s我卡了半个小时才过的……)

#include<cstdio>
#include<cmath>
#include<iostream>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cctype>
using namespace std;
typedef long long ll;
const int N=3e5+5;
inline ll read(){
    ll x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*w;
}
struct node{
    int to,nxt;
}e[N];
struct tree{
    int l,r,dis;
    ll val;
}tr[N];
int head[N],cnt,die[N],knight[N],rt[N];
ll h[N],f[N],a[N],v[N],lazy[N][3];
inline void add(int u,int v){
    e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;
}
inline void init(int x){
    lazy[x][0]=lazy[x][2]=0;
    lazy[x][1]=1;
}
inline void push(int x){
    if(!x)return;
    if(tr[x].l){
    tr[tr[x].l].val*=lazy[x][1];tr[tr[x].l].val+=lazy[x][0];
    knight[tr[x].l]+=lazy[x][2];
    lazy[tr[x].l][0]*=lazy[x][1];lazy[tr[x].l][1]*=lazy[x][1];
    lazy[tr[x].l][0]+=lazy[x][0];lazy[tr[x].l][2]+=lazy[x][2];
    }
    if(tr[x].r){
    tr[tr[x].r].val*=lazy[x][1];tr[tr[x].r].val+=lazy[x][0];
    knight[tr[x].r]+=lazy[x][2];
    lazy[tr[x].r][0]*=lazy[x][1];lazy[tr[x].r][1]*=lazy[x][1];
    lazy[tr[x].r][0]+=lazy[x][0];lazy[tr[x].r][2]+=lazy[x][2];
    }
    init(x);
}
int merge(int x,int y){
    if(!x||!y)return x+y;
    push(x);push(y);
    if(tr[x].val>tr[y].val)
    swap(x,y);
    tr[x].r=merge(tr[x].r,y);
    if(tr[tr[x].l].dis<tr[tr[x].r].dis)
    swap(tr[x].l,tr[x].r);
    tr[x].dis=tr[tr[x].r].dis+1;
    return x;
}
int del(int x){
    push(x);
    return merge(tr[x].l,tr[x].r);
}
void dfs(int u){
    for(int i=head[u];i;i=e[i].nxt){
    int v=e[i].to;
    if(v==f[u])continue;
    dfs(v);
    rt[u]=merge(rt[u],rt[v]);
    }
    while(rt[u]&&tr[rt[u]].val<h[u]){
    die[u]++;
    rt[u]=del(rt[u]);
    }
    if(rt[u]){
    knight[rt[u]]++;
    lazy[rt[u]][2]++;
    if(!a[u]){
        tr[rt[u]].val+=v[u];
        lazy[rt[u]][0]+=v[u];
    }
    else{
        tr[rt[u]].val*=v[u];
        lazy[rt[u]][0]*=v[u];
        lazy[rt[u]][1]*=v[u];
    }
    }
}
void upt(int x){
    if(!x)return;
    push(x);
    upt(tr[x].l);upt(tr[x].r);
}
int main(){
    int n=read(),m=read();
    for(int i=1;i<=n;i++)h[i]=read();
    for(int i=2;i<=n;i++){
    f[i]=read(),a[i]=read(),v[i]=read();
    add(f[i],i);
    }
    for(int i=1;i<=m;i++)init(i);
    for(int i=1;i<=m;i++){
    tr[i].val=read();
    int c=read();
    rt[c]=merge(i,rt[c]);
    }
    dfs(1);
    upt(rt[1]);
    for(int i=1;i<=n;i++)printf("%d\n",die[i]);
    for(int i=1;i<=n;i++)printf("%d\n",knight[i]);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-04-04 11:51  luyouqi233  阅读(192)  评论(0编辑  收藏  举报