【纪中集训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;
    }
    
posted @ 2019-03-28 20:15  大米饼  阅读(214)  评论(0编辑  收藏  举报