【BZOJ4372】烁烁的游戏(动态点分治)(树状数组)

传送门


题解:

建立点分树,每个分治中心开一个树状数组,表示它对所有到它距离为 t t t的点的贡献

每次修改直接暴跳分治树父亲修改,发现父子分治中心会对一些点有重复贡献,每个儿子开一个树状表示它的连通块内距离父亲不超过 t t t的点被重复贡献了多少。

询问暴跳点分树查每一个分治中心的树状数组即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	static cs int Rlen=1<<22|1;
	static char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	inline char peek(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1;}
	inline char ga(){while(!isalpha(peek()))++p1;return *p1++;}
	template<typename T>
	inline T get(){
		char c;T num;bool f=0;
		while(!isdigit(c=gc()))f=c=='-';num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs int N=1e5+7;

int n,m;

std::vector<int> G[N];
inline void adde(int u,int v){
	G[u].push_back(v);G[v].push_back(u);
} 

namespace ST{
	int in[N],dfn,d[N],mn[19][N<<1],Log[N<<1];
	void dfs(int u,int p){
		mn[0][in[u]=++dfn]=d[u]=d[p]+1;
		for(int re v:G[u])if(v!=p)dfs(v,u),mn[0][++dfn]=d[u];
	}
	inline void init(){
		dfs(1,0);for(int re i=2;i<=dfn;++i)Log[i]=Log[i>>1]+1;
		for(int re i=1;i<=Log[dfn];++i)
		for(int re j=1;j+(1<<i)-1<=dfn;++j)
		mn[i][j]=std::min(mn[i-1][j],mn[i-1][j+(1<<i-1)]); 
	}
	inline int dis(int u,int v){
		int l=in[u],r=in[v];if(l>r)std::swap(l,r);
		int t=Log[r-l+1];return d[u]+d[v]-2*std::min(mn[t][l],mn[t][r-(1<<t)+1]);
	}
}
using ST::dis;

int fa[N],ban[N],siz[N];
int rt,total,mx_siz;

void get_siz(int u,int p){
	siz[u]=1;for(int re v:G[u])
	if(v!=p&&!ban[v]){get_siz(v,u);siz[u]+=siz[v];}
}
void find_rt(int u,int p){
	int mx=total-siz[u];for(int re v:G[u])
	if(v!=p&&!ban[v]){find_rt(v,u);mx=std::max(mx,siz[v]);}
	if(mx<mx_siz){mx_siz=mx,rt=u;}
}
void get_rt(int u){
	get_siz(u,0);
	total=siz[u],mx_siz=0x3f3f3f3f;
	find_rt(u,0);
}

struct BIT{
	std::vector<int> a;int n;
	inline void init(int _n){n=_n+1;a.resize(n+1);}
	inline void add(int p,int v){for(p=std::min(p+1,n);p>=1;p^=p&-p)a[p]+=v;}
	inline int query(int p){int res=0;for(p=std::min(p+1,n);p<=n;p+=p&-p)res+=a[p];return res;}
}f1[N],f2[N];

int mxd;
void get_d(int u,int p,int d){
	mxd=std::max(d,mxd);
	for(int re v:G[u])if(v!=p&&!ban[v])get_d(v,u,d+1);
}

void DFS(int u){
	ban[u]=true;mxd=0;
	get_d(u,0,0);f1[u].init(mxd);
	for(int re v:G[u])if(!ban[v]){
		get_rt(v);fa[rt]=u;
		mxd=0;get_d(v,0,1);
		f2[rt].init(mxd);
		DFS(rt);
	}
}

inline int query(int u){
	int ans=0;
	for(int re v=u;v;v=fa[v]){
		ans+=f1[v].query(dis(u,v));
		if(fa[v])ans-=f2[v].query(dis(u,fa[v]));
	}
	return ans;
}

inline void modify(int u,int d,int w){
	for(int re v=u;v;v=fa[v]){
		f1[v].add(d-dis(u,v),w);
		if(fa[v])f2[v].add(d-dis(u,fa[v]),w);
	}
}

signed main(){
#ifdef zxyoi
	freopen("game.in","r",stdin);
#endif
	n=gi(),m=gi();
	for(int re i=1;i<n;++i)adde(gi(),gi());
	ST::init();get_rt(1);DFS(rt);
	while(m--){
		switch(ga()){
			case 'Q':cout<<query(gi())<<"\n";break;
			case 'M':{
				int u=gi(),d=gi(),w=gi();
				modify(u,d,w);break;
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值