题目
描述
(Byteland)王国的形状是一颗标号为 $ 1 o N $ 的树,每个城市(节点有财富值 $ W_i $ );
如果在(A)城市投下(X)杀伤力的炸弹,那么对于(B)城市收到的伤害为(frac{X}{2^{dis(A,B)} }) ;
伤害可以累加,当一个城市收到的伤害超过财富值,那么将会破产;
(dis(A,B))为两点的树上距离;
有(Q)个操作每个为:
(1 A X) , 在(A)放下(X)杀伤力的炸弹;
$ 2 A $ ,询问 $ A $ 的子树里已经破产的城市;
范围
(N , Q le 100000 , W_i , X le 32768) ;
题解
-
只要知道了每个点的破产时间就可以(dfs序 + BIT)维护答案;
-
算法1
-
用线段树维护每个点(bfs)序的(W),一次修改会影响到(bfs)序上(log^2V)个连续的区间;
-
同时维护最小的(W),当最小值小于(0)直接找到并更新(BIT),每个点只会被删除一次;
-
复杂度:(O(Q log N log^2V)); (优点是在线的)
-
算法二
-
考虑整体二分;
-
学到了一种比较好写的整体二分,但是常数较大:
-
主要时间复杂度在每次二分后的查找询问上;
-
时间复杂度:(O(Q log Q log^2V))
-
算法三
-
考虑改变修改的存储方式;
-
对每个节点开一个(logV)大小的表表示对某个距离的影响;
-
对于一个修改,更新其向上(logV)个表;
-
查询一个点时,直接查找其向上的(logV)个祖先的表并简单容斥;
-
用(vector)可持久化这些表;
-
接着套用整体二分只要移动指针就可以找到对应时间的表;
-
时间复杂度:(O(Qlog Q + Qlog^2 V ));
#include<bits/stdc++.h> #define ui unsigned int #define pb push_back using namespace std; const int N=100010; int n,m,o=1,hd[N],fa[N],st[N],ed[N],idx,a[N],t[N],cnt,tot,p[N],c[N],id[N],del[N]; struct Edge{int v,nt;}E[N<<1]; struct data{ int l,r,m; data(int _l=0,int _r=0):l(_l),r(_r){m=l+r>>1;}; }A[N]; struct query{int t,u;}B[N]; struct update{ui d[20];}NUL; vector<update>g[N]; vector<int>q[N]; char gc(){ static char*p1,*p2,s[1000000]; if(p1==p2)p2=(p1=s)+fread(s,1,1000000,stdin); return(p1==p2)?EOF:*p1++; } int rd(){ int x=0;char c=gc(); while(c<'0'||c>'9')c=gc(); while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=gc(); return x; } void adde(int u,int v){ E[o]=(Edge){v,hd[u]};hd[u]=o++; E[o]=(Edge){u,hd[v]};hd[v]=o++; } void dfs(int u){ st[u]=++idx; for(int i=hd[u],v;i;i=E[i].nt){ if((v=E[i].v)==fa[u])continue; fa[v]=u;dfs(v); }ed[u]=idx; } void add(int x){for(;x<=n;x+=x&-x)c[x]++;} int ask(int x){int re=0;for(;x;x-=x&-x)re+=c[x];return re;} int main(){ freopen("pang.in","r",stdin); freopen("pang.out","w",stdout); n=rd(); memset(NUL.d,0,sizeof(NUL.d)); for(int i=1;i<=n;++i)a[i]=rd(); for(int i=1;i<n;++i)adde(rd(),rd()); for(int i=1;i<=n;++i)g[i].pb(NUL),q[i].pb(0); dfs(1); m=rd(); for(int i=1,op,x,y;i<=m;++i){ op=rd(); if(op&1){ for(++cnt,x=rd(),y=rd();x&&y;x=fa[x],y>>=1){ t[x]++;q[x].pb(cnt); g[x].pb(g[x].back()); for(int j=0,z=y;j<=15&&z;++j,z>>=1)g[x][t[x]].d[j]+=z; } }else B[++tot]=(query){cnt,rd()}; } for(int i=1;i<=n;++i)A[i]=data(0,cnt+1); ++cnt; while(1){ for(int i=1;i<=n;++i)p[i]=0; for(int i=0;i<=cnt;++i)c[i]=0; for(int i=1;i<=n;++i)c[A[i].m]++; for(int i=1;i<=cnt;++i)c[i]+=c[i-1]; for(int i=n;i;--i)id[c[A[i].m]--]=i; int fg=0; for(int i=1;i<=n;++i){ data&tmp=A[id[i]];ui s=0; if(tmp.l==tmp.r)continue; for(int x=id[i],j=0;j<=15&&x;x=fa[x],++j){ while(p[x]<t[x]&&q[x][p[x]+1]<=tmp.m)p[x]++; s+=g[x][p[x]].d[j]; if(fa[x])s-=g[x][p[x]].d[j+2]; if(s>=a[id[i]])break; } if(s>=a[id[i]])tmp=data(tmp.l,tmp.m); else tmp=data(tmp.m+1,tmp.r); if(tmp.l<tmp.r)fg=1; } if(!fg)break; } for(int i=1;i<=cnt;++i)c[i]=0; for(int i=1;i<=n;++i)c[del[i]=A[i].m]++; for(int i=1;i<=cnt;++i)c[i]+=c[i-1]; for(int i=1;i<=n;++i)id[c[del[i]]--]=i; for(int i=1;i<=cnt;++i)c[i]=0; for(int i=1,j=1;i<=tot;++i){ while(j<=n&&del[id[j]]<=B[i].t)add(st[id[j++]]); printf("%d ",ask(ed[B[i].u])-ask(st[B[i].u]-1)); } return 0; }