P3261 JLOI2015 城池攻占
题目描述
小铭铭最近获得了一副新的桌游,游戏中需要用 \(m\) 个骑士攻占 \(n\) 个城池。
这 \(n\) 个城池用 \(1\) 到 \(n\) 的整数表示。除 \(1\) 号城池外,城池 \(i\) 会受到另一座城池 \(f_i\) 的管辖,其中 \(f_i<i\)。也就是说,所有城池构成了一棵有根树。
这 \(m\) 个骑士用 \(1\) 到 \(m\) 的整数表示,其中第 \(i\) 个骑士的初始战斗力为 \(s_i\),第一个攻击的城池为 \(c_i\)。
每个城池有一个防御值 \(h_i\),如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击管辖这座城池的城池,直到占领 \(1\) 号城池,或牺牲为止。
除 \(1\) 号城池外,每个城池 \(i\) 会给出一个战斗力变化参数 \((a_i,v_i)\)。若 \(a_i=0\),攻占城池 \(i\) 以后骑士战斗力会增加 \(v_i\);若 \(a_i=1\),攻占城池 \(i\) 以后,战斗力会乘以 \(v_i\)。
注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。
输入格式
第 \(1\) 行包含两个正整数 \(n,m\),表示城池的数量和骑士的数量。
第 \(2\) 行包含 \(n\) 个整数,其中第 \(i\) 个数为 \(h_i\),表示城池 \(i\) 的防御值。
第 \(3\sim n+1\) 行,每行包含三个整数。其中第 \(i+1\) 行的三个数为 \(f_i,a_i,v_i\),分别表示管辖这座城池的城池编号和两个战斗力变化参数。
第 \(n+2\sim n+m+1\) 行,每行包含两个整数。其中第 \(n+i\) 行的两个数为 \(s_i,c_i\),分别表示初始战斗力和第一个攻击的城池。
输出格式
输出 \(n+m\) 行,每行包含一个非负整数。其中前 \(n\) 行分别表示在城池 \(1\) 到 \(n\) 牺牲的骑士数量,后 \(m\) 行分别表示骑士 \(1\) 到 \(m\) 攻占的城池数量。
样例 #1
样例输入 #1
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
样例输出 #1
2
2
0
0
0
1
1
3
1
1
提示
对于 \(100\%\) 的数据,\(1\le n,m\le 3\times 10^5\),\(-10^{18}\le h_i,v_i,s_i\le 10^{18}\),\(1\le f_i<i,1\le c_i\le n,a_i\in\{0,1\}\),保证 \(a_i=1\) 时,\(v_i>0\),保证任何时候骑士战斗力值的绝对值不超过 \(10^{18}\)。
分析
从下到上遍历树,一直拿当前节点上的骑士团中最蒻的看看会不会死在这里,再把没死的放到父亲节点上去,并改变他们的战斗力。
骑士的能力是会变的,树上批量修改需要记得标记下传。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
struct node{//骑士战斗值,dist,左右儿子,死亡城池,出生城池,加标记,乘标记
int val,dist,ch[2],die,c,add,tim;
}t[300005];
struct city{//城池防御值,管辖城池,a,v,深度,当前节点骑士团的根,在这个城池死亡人数
int val,fa,a,v,dep,rt,ans;
}q[300005];
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
void pushdown(int x){//标记下传,遵循先乘后加原则
if(t[x].add||t[x].tim!=1){
if(t[x].ch[0]){
t[t[x].ch[0]].tim*=t[x].tim;
(t[t[x].ch[0]].add*=t[x].tim)+=t[x].add;
(t[t[x].ch[0]].val*=t[x].tim)+=t[x].add;
}
if(t[x].ch[1]){
t[t[x].ch[1]].tim*=t[x].tim;
(t[t[x].ch[1]].add*=t[x].tim)+=t[x].add;
(t[t[x].ch[1]].val*=t[x].tim)+=t[x].add;
}
t[x].add=0,t[x].tim=1;
}
}
int merge(int x,int y){//合并前记得先下传标记
if((!x)||(!y))return x|y;
pushdown(x),pushdown(y);
if(t[x].val>t[y].val)swap(x,y);
t[x].ch[1]=merge(t[x].ch[1],y);
if(t[t[x].ch[0]].dist<t[t[x].ch[1]].dist)swap(t[x].ch[0],t[x].ch[1]);
t[x].dist=t[t[x].ch[1]].dist+1;
return x;
}
signed main(){
n=read(),m=read();t[0].dist=-1,q[1].dep=1;
for(int i=1;i<=n;i++){
q[i].val=read();q[i].rt=-1;
}
for(int i=2;i<=n;i++){
q[i].fa=read(),q[i].a=read(),q[i].v=read();
q[i].dep=q[q[i].fa].dep+1;
}
for(int i=1;i<=m;i++){
t[i].val=read(),t[i].c=read();t[i].tim=1;
if(q[t[i].c].rt!=-1)q[t[i].c].rt=merge(i,q[t[i].c].rt);//合并出生城池相同的士兵
else q[t[i].c].rt=i;
}
for(int i=n;i;i--){//逐个枚举城池
while(q[i].rt!=-1){
if(t[q[i].rt].val<q[i].val){//如果堆顶小于防御值
t[q[i].rt].die=i,pushdown(q[i].rt);//标记死亡后下传
if(!t[q[i].rt].ch[0])q[i].rt=-1;
else q[i].rt=merge(t[q[i].rt].ch[0],t[q[i].rt].ch[1]);//合并左右儿子
}else break;//最小的都没死,则其他的也不会死
}
if(i!=1&&q[i].rt!=-1){//更改骑士能力值
if(q[i].a)t[q[i].rt].tim*=q[i].v,t[q[i].rt].add*=q[i].v,t[q[i].rt].val*=q[i].v;
else t[q[i].rt].add+=q[i].v,t[q[i].rt].val+=q[i].v;
pushdown(q[i].rt);
if(q[q[i].fa].rt!=-1)q[q[i].fa].rt=merge(q[q[i].fa].rt,q[i].rt);
else q[q[i].fa].rt=q[i].rt;
}
}
for(int i=1;i<=m;i++)q[t[i].die].ans++;//统计答案
for(int i=1;i<=n;i++)cout<<q[i].ans<<endl;
for(int i=1;i<=m;i++)cout<<q[t[i].c].dep-q[t[i].die].dep<<endl;
return 0;
}

浙公网安备 33010602011771号