【bzoj4372】烁烁的游戏

Portal -->bzoj4372

Solution

  感觉自己动态点分治好像没有学好qwq今天借这题来补个档qwq

  其实所谓的动态点分治大概就是。。和点分一样的套路,但是不同的是我们要更进一步利用好每次找重心而产生的优秀性质:深度期望\(log\)

  我们考虑按照点分治那样每次找重心,但是不同的是我们要记录当前层递归的前一层的重心(记为\(pre[rt]=fa\),其中\(fa\)是上一层递归找到的重心,\(rt\)是当前层找到的重心)

  然后每个重心会有一个管辖范围(具体一点就是\(rt\)是在哪个范围内为重心),我们考虑将整个管辖范围内点的对应信息记录到这个重心上(在不同的题里面记录方式不同,比如说这题的话就是对于每一个重心开一棵线段树或者树状数组来记录)

  然后每次查询的话(比如说查询点\(x\)),我们就从\(x\)开始,每次跳到上一层递归的重心,调用重心中存储的与\(x\)有关的值,直到根,因为建点分树的方式是每层找重心,所以期望深度是\(log\)级别的

  修改的话同理(比如说修改点\(x\)),也是从\(x\)开始,每次跳到上一层递归的重心,修改重心中存储的与\(x\)有关的值,直到根,同样也是\(log\)级别的

  听起来很暴力

  但是。。点分治本来不就是一个让你的暴力跑得飞快的黑科技吗哈哈哈哈哈

  

  回到这道题

  这题中我们需要维护的显然是每个点的权值,考虑在每个重心处维护的信息是管辖范围内每个点修改了多少(类似打了个修改标记一样),然后因为修改与这个点本身的\(dep\)有关(因为钦定了每条边的边权是\(1\)嘛那就是深度咯),所以我们考虑按照管辖范围内每个点到重心的距离从小到大排序为下标,用线段树或者树状数组来维护答案

  这样有一个好处,就是修改的时候比较方便操作。我们考虑在一开始建点分树的时候,将每个重心的管辖范围内的每个点排完序之后的顺序记录下来(将这个数组记为\(id\)),同时记录排完序之后的到重心的距离(将这个数组记为\(rec\)),那么我们只需要在这个记录距离的\(rec\)数组里二分一下,找到满足题目条件的最靠后的那个点(其实就是直接upper_bound一下就好了)在记录数组中的下标\(pos\),然后将区间\([1,pos]\)中的点值全部加上\(w\)就好了

  这里需要注意的是,因为我们在\(rec\)数组中存的是到当前层重心的距离,所以每次在\(rec\)中二分的时候带进去的查询值应该要减去目标点到当前层重心的距离

  接下来还有一个小问题,就是由于当前重心\(rt\)的管辖范围也在上一层重心的管辖范围内,所以在往上跳修改的时候可能会有一部分的点被修改了两次,那么这个时候我们需要小小的容斥一下,具体一点的话就是:假设我们当前的修改操作是\(M\ x\ d\ w\),当前层的重心是\(rt\),我们要将\(pre[rt]\)中与\(rt\)中计算重复的部分减去,那么我们应该将\(rec\)\(\in [0,d-dis(pre[rt],x)-1]\)的这些点在\(rt\)这层的修改值去掉,因为在\(pre[rt]\)这层会修改到(然而实际操作起来的时候我们可以开两棵线段树(或者树状数组),一棵记录加的标记,一棵记录容斥要减掉的标记)

  最后就是查询,查询相对来说就会没那么繁琐了,因为在修改中我们已经容斥过了,所以查询的时候直接从目标点开始一直往根跳然后每次将该层的重心中记录的标记累加进去就好了

​  不过如果写线段树的话。。空间和时间都会相对来说吃紧一点(特别对于我这种辣鸡自带常数的选手qwq)所以保险一点还是写树状数组吧反正。。这题是区间修改单点查询嘛问题不大(所以我到底是怎么做到明明写的是树状数组还写了那么长的qwq)

  然而实际上。。貌似写线段树的话如果改成单点修改区间查询这样的方式(其实就是树状数组那种套路),不需要标记下传之类的也能做到常数十分优秀并且代码简洁

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define TOP 20
#define vct vector<int>
using namespace std;
const int N=1e5+10,BIT=N*17*2;
struct xxx{
	int y,nxt;
}a[N*2];
char s[10];
vct rec[N][2];
int f[N][TOP+1],dep[N],vis[N];
int h[N],sz[N],mx[N],belong[N],tmprec[N],tmpdep[N];
int df_dep[N],pre[N],df_t[N],df_sz[N];
int id[TOP+1][N][2];
int n,m,tot,rt,rt_sz,ans;
namespace Bit{/*{{{*/
	int c[BIT],st[N][2],mx[N][2];
	int tot=0;
	void _update(int which,int x,int delta,int op){
		for (;x<=mx[which][op];x+=x&-x)
			c[st[which][op]+x]+=delta;
	}
	void update(int which,int op,int l,int r,int delta){
		_update(which,l,delta,op);
		if (r<mx[which][op]) _update(which,r+1,-delta,op);
	}
	int query(int which,int op,int x){
		int ret=0;
		for (;x;x-=x&-x) ret+=c[st[which][op]+x];
		return ret;
	}
}/*}}}*/
//orignal tree{{{
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void predfs(int fa,int x,int d){
	int u;
	dep[x]=d; f[x][0]=fa;
	for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa) continue;
		predfs(x,u,d+1);
	}
}
int get_lca(int x,int y){
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=TOP;i>=0;--i)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=TOP;i>=0;--i)
		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int get_dis(int x,int y){
	int lca=get_lca(x,y);
	return dep[x]+dep[y]-2*dep[lca];
}
/*}}}*/
//get_rt{{{
void get_sz(int fa,int x){
	int u;
	sz[x]=1; mx[x]=0;
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa||vis[u]) continue;
		get_sz(x,u);
		sz[x]+=sz[u];
		if (sz[u]>mx[x]) mx[x]=sz[u];
	}
}
void get_rt(int Rt,int fa,int x){
	int u;
	mx[x]=max(mx[x],sz[Rt]-sz[x]);
	if (mx[x]<=rt_sz) rt=x,rt_sz=mx[x];
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa||vis[u]) continue;
		get_rt(Rt,x,u);
	}
}/*}}}*/
//df-tree{{{
void dfs(int fa,int x,int d){
	int u;	
	tmprec[++tmprec[0]]=x; tmpdep[x]=d;
	for (int i=h[x];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (u==fa||vis[u]) continue;
		dfs(x,u,d+1);
	}
}
bool cmp(int x,int y){return tmpdep[x]<tmpdep[y];}
void get_lis(int x,int rt,int which_t,int op){
	tmprec[0]=0;
	Bit::st[rt][op]=Bit::tot;
	if (op==0)
		dfs(0,rt,0),Bit::mx[rt][op]=tmprec[0];
	else 
		dfs(0,x,0),Bit::mx[rt][op]=Bit::mx[rt][0];
	Bit::tot+=tmprec[0];

	sort(tmprec+1,tmprec+1+tmprec[0],cmp);
	rec[rt][op].resize(tmprec[0]);
	for (int i=1;i<=tmprec[0];++i){
		id[which_t][tmprec[i]][op]=i;
		rec[rt][op][i-1]=tmpdep[tmprec[i]];
	}
}
void solve(int fa,int x,int Sz){
	int u,Rt;
	rt=0,rt_sz=n;
	get_sz(0,x);
	get_rt(x,0,x);
	Rt=rt;

	df_t[Rt]=df_t[fa]+1; df_sz[Rt]=Sz;
	pre[Rt]=fa;
	get_lis(x,Rt,df_t[Rt],0);
	get_lis(x,Rt,df_t[Rt],1);
	vis[Rt]=true;
	for (int i=h[Rt];i!=-1;i=a[i].nxt){
		u=a[i].y;
		if (vis[u]) continue;
		solve(Rt,u,sz[u]>sz[Rt]?Sz-sz[Rt]:sz[u]);
	}
}
void modify(int x,int aim,int d,int delta){
	int dis=d-get_dis(x,aim),pos;
	pos=upper_bound(rec[x][0].begin(),rec[x][0].end(),dis)-rec[x][0].begin();
	Bit::update(x,0,1,pos,delta);
	if (pre[x]){
		dis=d-get_dis(pre[x],aim)-1;
		if (dis>=0){
			pos=upper_bound(rec[x][1].begin(),rec[x][1].end(),dis)-rec[x][1].begin();
			Bit::update(x,1,1,pos,-delta);
		}
		modify(pre[x],aim,d,delta);
	}
}
int query(int x,int aim){
	int ret=Bit::query(x,0,id[df_t[x]][aim][0]);
	if (pre[x]){
		ret+=Bit::query(x,1,id[df_t[x]][aim][1]);
		ret+=query(pre[x],aim);
	}
	return ret;
}
/*}}}*/

int main(){
#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
#endif
	int x,y,d,w;
	scanf("%d%d",&n,&m);
	memset(h,-1,sizeof(h));
	tot=0;
	for (int i=1;i<n;++i){
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	predfs(0,1,1);
	solve(0,1,n);
	for (int i=1;i<=m;++i){
		scanf("%s",s);
		if (s[0]=='Q'){
			scanf("%d",&x);
			ans=query(x,x);
			printf("%d\n",ans);
		}
		else{
			scanf("%d%d%d",&x,&d,&w);
			modify(x,x,d,w);
		}
	}
}
posted @ 2018-07-23 21:08  yoyoball  阅读(467)  评论(0编辑  收藏  举报