【纪中集训2019.3.27】胖
题目
描述
\(Byteland\)王国的形状是一颗标号为 $ 1 \to 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\n",ask(ed[B[i].u])-ask(st[B[i].u]-1)); } return 0; }