[JLOI2015]城池攻占
题目描述
小铭铭最近获得了一副新的桌游,游戏中需要用 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。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。
输入格式:
第 1 行包含两个正整数 n;m,表示城池的数量和骑士的数量。
第 2 行包含 n 个整数,其中第 i 个数为 hi,表示城池 i 的防御值。
第 3 到 n +1 行,每行包含三个整数。其中第 i +1 行的三个数为 fi;ai;vi,分别表示管辖这座城池的城池编号和两个战斗力变化参数。
第 n +2 到 n + m +1 行,每行包含两个整数。其中第 n + i 行的两个数为 si;ci,分别表示初始战斗力和第一个攻击的城池。
输出格式:
输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士数量,后 m 行分别表示骑士 1 到 m 攻占的城池数量。
输出样例:
2 2 0 0 0 1 1 3 1 1
说明
对于 100% 的数据,1 <= n;m <= 300000; 1 <= fi<i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18; ai等于1或者0;
当 ai =1 时,vi > 0;保证任何时候骑士战斗力值的绝对值不超过 10^18。
思路
我们看一下题目,题目之中说,如果当前骑士如果攻占了当前的城池,他就会继续前往下一个城池,那么这样我们就可以选择一个可以进行合并的数据结构。继续读题,我们发现如果当前骑士的能力值小于当前的城池,他就会被击杀,这样我们想到可以运用小根堆,这样我们就可以快速杀死所有能力小于城池的骑士。两者结合起来就是可并堆。然而我们依旧需要继续读题,题目之中说了,我们每一次到一个城池,能打下这个城池的所有骑士的能力值都会对应有所改变,如果我们每一次都暴力修改,是不是太慢了?因为修改程度是一样的,所以堆的内部结构不会改变,这样的话,我们可以用一个数组,类似于lazy标记,表示以当前节点为根的堆所有点的变化量,当然这个数组应该由两部分组成,一部分来存乘的数值,另一部分存加的数值。修改就好了。注意先乘后加!!!
代码
手写栈的写法(有的oj不开全栈)
// luogu-judger-enable-o2 #include <stdio.h> #include <algorithm> using namespace std; int order[300001]; int lson[300001]; int rson[300001]; int dis[300001]; long long need[300001]; long long num[300001]; int head[300001]; int to[300001]; int nxt[300001]; long long v[300001]; int fro[300001]; int level[300001]; int ans[300001]; int ans2[300001]; bool is[300001]; long long oper1[300001]; long long oper2[300001]; int n,m,idx; int str[300001]; int from[300001]; void add(int x,int y) { nxt[++idx]=head[x]; head[x]=idx; to[idx]=y; } void change(int p) { num[p]*=oper1[p]; num[p]+=oper2[p]; oper1[p]=1; oper2[p]=0; } void pushdown(int p) { if(lson[p]) { oper1[lson[p]]*=oper1[p]; oper2[lson[p]]*=oper1[p]; oper2[lson[p]]+=oper2[p]; } if(rson[p]) { oper1[rson[p]]*=oper1[p]; oper2[rson[p]]*=oper1[p]; oper2[rson[p]]+=oper2[p]; } change(p); } int merge(int x,int y) { if(!x) return y; if(!y) return x; pushdown(x);pushdown(y); if(num[y]<num[x]) swap(x,y); rson[x]=merge(rson[x],y); if(dis[rson[x]]>dis[lson[x]]) swap(lson[x],rson[x]); dis[x]=dis[rson[x]]+1; return x; } int check(int pla,int p) { pushdown(p); while(need[pla]>num[p]&&p) { ans[p]=level[fro[p]]-level[pla]; p=merge(lson[p],rson[p]); ans2[pla]++; pushdown(p); } return p; } void dfs(int p) { int l,hed; l=idx=0,str[++idx]=p,level[1]=1; while(l<idx) { p=str[++l]; for(int i=head[p];i;i=nxt[i]) str[++idx]=to[i],from[idx]=p,level[to[i]]=level[p]+1; } while(idx) { p=str[idx]; order[p]=check(p,order[p]); if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p]; if(!is[p]) oper2[order[p]]+=v[p]; order[from[idx]]=merge(order[from[idx]],order[p]); idx--; }/* int tmp; level[p]=level[from]+1; for(int i=head[p];i;i=nxt[i]) tmp=dfs(to[i],p),order[p]=merge(order[p],tmp); order[p]=check(p,order[p]); if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p]; if(!is[p]) oper2[order[p]]+=v[p]; return order[p];*/ } int main() { dis[0]=-1; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&need[i]); for(int i=2;i<=n;i++) { int a,b; scanf("%d%d%lld",&a,&b,&v[i]); add(a,i); if(b==1) is[i]=true; } for(int i=1;i<=m;i++) { int b; oper1[i]=1; scanf("%lld%d",&num[i],&b); order[b]=merge(order[b],i); fro[i]=b; } dfs(1); need[0]=((long long)1e18+1); check(0,order[1]); for(int i=1;i<=n;i++) printf("%d ",ans2[i]); for(int i=1;i<=n;i++) printf("%d ",ans[i]); }
运用系统栈的写法(有的oj开全栈)
#include <stdio.h> #include <algorithm> using namespace std; int order[300001]; int lson[300001]; int rson[300001]; int dis[300001]; long long need[300001]; long long num[300001]; int head[300001]; int to[300001]; int nxt[300001]; long long v[300001]; int fro[300001]; int level[300001]; int ans[300001]; int ans2[300001]; bool is[300001]; long long oper1[300001]; long long oper2[300001]; int n,m,idx; int str[300001]; int from[300001]; void add(int x,int y) { nxt[++idx]=head[x]; head[x]=idx; to[idx]=y; } void change(int p) { num[p]*=oper1[p]; num[p]+=oper2[p]; oper1[p]=1; oper2[p]=0; } void pushdown(int p) { if(lson[p]) { oper1[lson[p]]*=oper1[p]; oper2[lson[p]]*=oper1[p]; oper2[lson[p]]+=oper2[p]; } if(rson[p]) { oper1[rson[p]]*=oper1[p]; oper2[rson[p]]*=oper1[p]; oper2[rson[p]]+=oper2[p]; } change(p); } int merge(int x,int y) { if(!x) return y; if(!y) return x; pushdown(x);pushdown(y); if(num[y]<num[x]) swap(x,y); rson[x]=merge(rson[x],y); if(dis[rson[x]]>dis[lson[x]]) swap(lson[x],rson[x]); dis[x]=dis[rson[x]]+1; return x; } int check(int pla,int p) { pushdown(p); while(need[pla]>num[p]&&p) { ans[p]=level[fro[p]]-level[pla]; p=merge(lson[p],rson[p]); ans2[pla]++; pushdown(p); } return p; } int dfs(int p,int from) {/* int l,hed; l=idx=0; str[++idx]=p; while(l<idx) { p=str[++l]; for(int i=head[p];i;=nxt[i]) str[++idx]=p,from[idx]=p; } while(idx) { p=str[idx--]; hed=check(p,order[p]); if(is[p]) oper1[hed]*=v[p],oper2[hed]*=v[p]; if(!is[p]) oper2[hed]+=v[p]; order[p]=merge() }*/ int tmp; level[p]=level[from]+1; for(int i=head[p];i;i=nxt[i]) tmp=dfs(to[i],p),order[p]=merge(order[p],tmp); order[p]=check(p,order[p]); if(is[p]) oper1[order[p]]*=v[p],oper2[order[p]]*=v[p]; if(!is[p]) oper2[order[p]]+=v[p]; return order[p]; } int main() { dis[0]=-1; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",&need[i]); for(int i=2;i<=n;i++) { int a,b; scanf("%d%d%lld",&a,&b,&v[i]); add(a,i); if(b==1) is[i]=true; } for(int i=1;i<=m;i++) { int b; oper1[i]=1; scanf("%lld%d",&num[i],&b); order[b]=merge(order[b],i); fro[i]=b; } int hed=dfs(1,0); need[0]=((long long)1e18+1); check(0,hed); for(int i=1;i<=n;i++) printf("%d ",ans2[i]); for(int i=1;i<=n;i++) printf("%d ",ans[i]); }