BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆
欢迎访问~原文出处——博客园-zhouzhendong
去博客园看该题解
题目传送门 - BZOJ4003
题意概括
题意有点复杂,直接放原题了。
小铭铭最近获得了一副新的桌游,游戏中需要用 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。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。
题解
从树根跑dfs。
对于每一个子树,合并它的所有子树所代表的堆。
死掉的就弹出就可以了。
然后修改只需要打两个懒标记就可以了。
代码
#include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cmath> #include <vector> using namespace std; typedef long long LL; const int N=300005; int n,m; vector <int> son[N]; int c[N],ls[N],rs[N],npl[N],root[N],ans1[N],ans2[N],depth[N]; LL h[N],op[N],v[N],val[N],add[N],times[N]; void pushson(int x,LL Add,LL Times){ val[x]=val[x]*Times+Add; add[x]=add[x]*Times+Add; times[x]*=Times; } void pushdown(int x){ if (ls[x]) pushson(ls[x],add[x],times[x]); if (rs[x]) pushson(rs[x],add[x],times[x]); add[x]=0,times[x]=1; } int merge(int a,int b){ if (!a||!b) return a+b; if (val[a]>val[b]) swap(a,b); pushdown(a); rs[a]=merge(rs[a],b); if (npl[rs[a]]>npl[ls[a]]) swap(rs[a],ls[a]); npl[a]=npl[rs[a]]+1; return a; } void pop(int &x){ pushdown(x); x=merge(ls[x],rs[x]); } void dfs(int rt){ for (int i=0;i<son[rt].size();i++){ int s=son[rt][i]; depth[s]=depth[rt]+1; dfs(s); root[rt]=merge(root[rt],root[s]); } while (root[rt]&&val[root[rt]]<h[rt]){ ans2[root[rt]]=rt; ans1[rt]++; pop(root[rt]); } if (op[rt]==0) pushson(root[rt],v[rt],1); else pushson(root[rt],0,v[rt]); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%lld",&h[i]); for (int i=1;i<=n;i++) son[i].clear(); for (int i=2,fa;i<=n;i++){ scanf("%d%lld%lld",&fa,&op[i],&v[i]); son[fa].push_back(i); } memset(root,0,sizeof root); for (int i=1;i<=m;i++){ scanf("%lld%d",&val[i],&c[i]); ls[i]=rs[i]=npl[i]=add[i]=0,times[i]=1; root[c[i]]=merge(root[c[i]],i); } memset(ans1,0,sizeof ans1); memset(ans2,0,sizeof ans2); depth[1]=1; dfs(1); for (int i=1;i<=n;i++) printf("%d\n",ans1[i]); for (int i=1;i<=m;i++) printf("%d\n",depth[c[i]]-depth[ans2[i]]); return 0; }