[bzoj4372]烁烁的游戏

[bzoj4372]烁烁的游戏


动态点分,需要注意的是我们对于每个点要开两个数组,另外一个相当于一个容斥,即把他父亲上的减掉。

  • 代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,inf=1e9;
char getopt(){
	char x=0;while(x<'A'||x>'Z')x=getchar();
	return x;
}
char opt;
struct data{
	int lch,rch,x;
}t[N*60];
int n,m,root[N],root1[N],hed[N],nxt[N],to[N],ans,sum,rt,tot,sz;
int f[N],mi[20],anc[N][20],size[N],dep[N],vis[N],belong[N];

void getroot(int x,int pre){
	f[x]=0,size[x]=1;
	for(int i=hed[x];i;i=nxt[i]){
		if(to[i]==pre||vis[to[i]])continue;
		getroot(to[i],x);
		size[x]+=size[to[i]];
		f[x]=max(f[x],size[to[i]]);
	}
	f[x]=max(f[x],sum-size[x]);
	if(f[x]<f[rt])rt=x;
}
void dfs(int x,int fthr){
	dep[x]=dep[fthr]+1;
	for(int i=1;i<=17;i++){
		if(dep[x]-mi[i]<0)break;
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	for(int i=hed[x];i;i=nxt[i]){
		if(to[i]==fthr)continue;
		anc[to[i]][0]=x;
		dfs(to[i],x);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	int k=dep[x]-dep[y];
	for(int i=0;i<=17;i++)
		if((k>>i)&1)x=anc[x][i];
	if(x==y)return x;
	for(int i=17;i>=0;i--){
		if(anc[x][i]!=anc[y][i])
			x=anc[x][i],y=anc[y][i];
	}
	return anc[x][0];
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}

void build(int x){
	vis[x]=1;
	for(int i=hed[x];i;i=nxt[i]){
		if(vis[to[i]])continue;
		rt=0;sum=size[to[i]];
		getroot(to[i],x);
		belong[rt]=x;
		build(rt);
	}
}

void insert(int &i,int l,int r,int x,int d){
	if(!i)i=++sz;
	t[i].x+=d;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(x<=mid)insert(t[i].lch,l,mid,x,d);
	else insert(t[i].rch,mid+1,r,x,d);
}

int qry(int now,int l,int r,int ll,int rr){
	if(ll<=l&&r<=rr)return t[now].x;
	int mid=(l+r)>>1;
	int ans=0;
	if(ll<=mid) ans+=qry(t[now].lch,l,mid,ll,rr);
	if(rr>mid) ans+=qry(t[now].rch,mid+1,r,ll,rr);
	return ans;
}
void change(int u,int x,int d,int w){
	int D=dis(u,x);
	if(d-D>=0)insert(root[u],0,n,d-D,w);
	int v=belong[u];
	if(!v)return;
	D=dis(x,v);
	if(d-D>=0)insert(root1[u],0,n,d-D,w);
	change(v,x,d,w);
}

void calc(int u,int x){
	if(u==x)ans+=qry(root[u],0,n,0,n);
	else{
		int d=dis(u,x);
		ans+=qry(root[u],0,n,d,n);
	}
	if(!belong[u])return;
	int d=dis(belong[u],x);
	ans-=qry(root1[u],0,n,d,n);
	calc(belong[u],x);
}
void add(int x,int y){
	tot++;nxt[tot]=hed[x],to[tot]=y;hed[x]=tot;
	tot++;nxt[tot]=hed[y],to[tot]=x;hed[y]=tot;
}

int main()
{
	mi[0]=1;
	for(int i=1;i<=1;i++)mi[i]=mi[i-1]<<1;
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);
	}
	dfs(1,0);
	rt=0;f[0]=inf;sum=n;
	getroot(1,0);build(rt);
	while(m--){
		opt=getopt();
		int x,d,w;
		if(opt=='Q'){
			scanf("%d",&x);
			ans=0;
			calc(x,x);
			printf("%d\n",ans);
		}else{
			scanf("%d%d%d",&x,&d,&w);
			d=min(d,n);
			change(x,x,d,w);
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值