这题有多种做法,一种是倍增预处理出每个点往上走2^i步最少需要的初始战斗力,一种是裸的启发式合并带标记splay。
每个点合并能攻占其儿子的所有骑士,删去所有无法攻占这个城市的骑士并记录答案。
注意到splay每次实际上只需要取出最小的元素判断是否牺牲,这显然可以用堆维护。
关于可并堆打标记:和线段树打标记一样,维护乘法标记a和加法标记b,所有元素表示为ax+b,用同样的方法下传。
注意merge前要先push。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=300010; 9 int n,m,cnt,a[N],fa[N],rt[N],bg[N],ed[N],ls[N],rs[N],dis[N],dep[N],dead[N],to[N],nxt[N],h[N]; 10 ll mul[N],plu[N],v[N],p[N],d[N]; 11 void add(int u,int v){ to[++cnt]=v; nxt[cnt]=h[u]; h[u]=cnt; } 12 13 void put(int x,ll a,ll b){ 14 if (!x) return; 15 v[x]=v[x]*a+b; mul[x]*=a; plu[x]=plu[x]*a+b; 16 } 17 18 void push(int x){ 19 put(ls[x],mul[x],plu[x]); put(rs[x],mul[x],plu[x]); 20 mul[x]=1; plu[x]=0; 21 } 22 23 int merge(int x,int y){ 24 if (!x || !y) return x+y; 25 push(x); push(y); 26 if (v[x]>v[y]) swap(x,y); 27 rs[x]=merge(rs[x],y); 28 if (dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]); 29 dis[x]=dis[rs[x]]+1; return x; 30 } 31 32 int pop(int x){ push(x); return merge(ls[x],rs[x]); } 33 34 void dfs(int x,int fa){ 35 dep[x]=dep[fa]+1; 36 For(i,x) dfs(k=to[i],x),rt[x]=merge(rt[x],rt[k]); 37 while (rt[x] && v[rt[x]]<d[x]) 38 dead[x]++,ed[rt[x]]=x,rt[x]=pop(rt[x]); 39 if (!a[x]) put(rt[x],1,p[x]); else put(rt[x],p[x],0); 40 } 41 42 int main(){ 43 freopen("bzoj4003.in","r",stdin); 44 freopen("bzoj4003.out","w",stdout); 45 scanf("%d%d",&n,&m); 46 rep(i,1,n) scanf("%lld",&d[i]); 47 rep(i,2,n) scanf("%d%d%lld",&fa[i],&a[i],&p[i]),add(fa[i],i); 48 rep(i,1,m) mul[i]=1,scanf("%lld%d",&v[i],&bg[i]),rt[bg[i]]=merge(rt[bg[i]],i); 49 dfs(1,0); 50 rep(i,1,n) printf("%d ",dead[i]); 51 rep(i,1,m) printf("%d ",dep[bg[i]]-dep[ed[i]]); 52 return 0; 53 }