[BZOJ4372]烁烁的游戏

完全不会写动态点分治,还有救吗?急,在线等

先建出点分树,每个节点$x$存它管辖范围内的所有点,按以$x$为根时的深度为关键字排序

修改$(x,d,w)$:对于$x$在点分树上的祖先$f$,若$\text{dis}_{f,x}\leq d$,则把深度在$\left[0,d-\text{dis}_{f,x}\right]$内的点$+w$

但是这样会算重,考虑容斥,记$f$在点分树上向$x$走一步是$p$,$p$的管辖范围内与$f$在原树中相邻的点为$t$,那么我们需要容斥掉的是以$t$为根,深度在$\left[0,d-\text{dis}_{p,x}-1\right]$内的点

这两个值在每个节点开两个树状数组存就可以了,查询时顺着点分树往上跳顺便统计答案即可

存节点在某棵点分树子树中的标号时用map会被卡常,因为一个点在点分树上只有$O(\log_2n)$个祖先,所以直接开一个$n\times\log_2n$的数组存就可以了

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
const int inf=2147483647;
int lowbit(int x){return x&-x;}
int h[100010],nex[200010],to[200010],M;
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
int dfn[100010],dep[100010],f[200010][18],lg[200010];
void dfs(int fa,int x){
	f[++M][0]=x;
	dfn[x]=M;
	dep[x]=dep[fa]+1;
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa){
			dfs(x,to[i]);
			f[++M][0]=x;
		}
	}
}
int mind(int x,int y){return dep[x]<dep[y]?x:y;}
int query(int l,int r){
	int k=lg[r-l+1];
	return mind(f[l][k],f[r-(1<<k)+1][k]);
}
int lca(int x,int y){
	if(dfn[x]>dfn[y])swap(x,y);
	return query(dfn[x],dfn[y]);
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
struct bit{
	int*s,n;
	void resize(int x){
		n=x;
		s=new int[n+1];
		memset(s,0,(n+1)<<2);
	}
	void add(int x,int v){
		while(x<=n){
			s[x]+=v;
			x+=lowbit(x);
		}
	}
	int query(int x){
		int res=0;
		while(x){
			res+=s[x];
			x-=lowbit(x);
		}
		return res;
	}
	void add(int l,int r,int v){
		add(l,v);
		if(r<n)add(r+1,-v);
	}
}b1[100010],b2[100010];
bool v[100010];
int siz[100010],n;
void dfs1(int fa,int x){
	siz[x]=1;
	n++;
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa&&!v[to[i]]){
			dfs1(x,to[i]);
			siz[x]+=siz[to[i]];
		}
	}
}
int mn,cn;
void dfs2(int fa,int x){
	int i,k=0;
	for(i=h[x];i;i=nex[i]){
		if(to[i]!=fa&&!v[to[i]]){
			dfs2(x,to[i]);
			k=max(k,siz[to[i]]);
		}
	}
	k=max(k,n-siz[x]);
	if(k<mn){
		mn=k;
		cn=x;
	}
}
int fa[100010];
struct pr{
	int x,d;
	pr(int a=0,int b=0){x=a;d=b;}
};
bool operator<(pr a,pr b){return a.d==b.d?a.x<b.x:a.d<b.d;}
vector<pr>s1[100010],s2[100010];
vector<pr>::iterator it;
void dfs3(vector&son,int fa,int x,int d){
	son.push_back(pr(x,d));
	for(int i=h[x];i;i=nex[i]){
		if(to[i]!=fa&&!v[to[i]])dfs3(son,x,to[i],d+1);
	}
}
int p1[18][100010],p2[18][100010],d2[100010];
void solve(int f,int x,int d){
	int i;
	n=0;
	dfs1(0,x);
	mn=inf;
	dfs2(0,x);
	fa[cn]=f;
	d2[cn]=d;
	dfs3(s1[cn],0,cn,0);
	sort(s1[cn].begin(),s1[cn].end());
	i=0;
	for(it=s1[cn].begin();it!=s1[cn].end();it++)p1[d][it->x]=++i;
	b1[cn].resize(n);
	dfs3(s2[cn],0,x,0);
	sort(s2[cn].begin(),s2[cn].end());
	i=0;
	for(it=s2[cn].begin();it!=s2[cn].end();it++)p2[d][it->x]=++i;
	b2[cn].resize(n);
	x=cn;
	v[x]=1;
	for(i=h[x];i;i=nex[i]){
		if(!v[to[i]])solve(x,to[i],d+1);
	}
}
#define pos(s,v) upper_bound(s[u].begin(),s[u].end(),pr(inf,v))-s[u].begin()
void modify(int x,int d,int w){
	int u,l;
	l=0;
	for(u=x;fa[u];u=fa[u]){
		if(d-l>=0)b1[u].add(1,pos(s1,d-l),w);
		l=dis(fa[u],x);
		if(d-l-1>=0)b2[u].add(1,pos(s2,d-l-1),-w);
	}
	if(d-l>=0)b1[u].add(1,pos(s1,d-l),w);
}
int query(int x){
	int u,s=0;
	for(u=x;fa[u];u=fa[u])s+=b2[u].query(p2[d2[u]][x])+b1[u].query(p1[d2[u]][x]);
	s+=b1[u].query(p1[d2[u]][x]);
	return s;
}
int main(){
	int n,m,i,j,x,y,z;
	char s[5];
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	M=0;
	dfs(0,1);
	for(i=2;i<=M;i++)lg[i]=lg[i>>1]+1;
	for(j=1;j<18;j++){
		for(i=1;i+(1<<j)-1<=M;i++)f[i][j]=mind(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	}
	solve(0,1,0);
	while(m--){
		scanf("%s%d",s,&x);
		if(s[0]=='Q')
			printf("%d\n",query(x));
		else{
			scanf("%d%d",&y,&z);
			modify(x,y,z);
		}
	}
}

转载于:https://www.cnblogs.com/jefflyy/p/9356810.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值