Description
背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠。
给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠。
烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠。皮皮鼠会被烁烁吸引,所以会一直待在节点上不动。
烁烁很好奇,在当前时刻,节点u有多少个他的好朋友—皮皮鼠。
大意:
给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:
Q x:询问x的点权。
M x d w:将树上与节点x距离不超过d的节点的点权均加上w。
n,m<=105,|w|<=104
注意:w不一定为正整数,因为烁烁可能把皮皮鼠吓傻了。
Solution
我终于会动态点分治辣
我们把点分治时下一层的分治中心作为此时分治中心的儿子,这样可以得到一棵新的树,我们称之为点分树
这棵新的树有许多优秀的性质,比如只有log层。于是一些跑不过的暴力就可以在点分树上套着数据结构跑了
在这题我们可以在每个节点x开一棵以 [x的点分树子树内到x距离] 作为下标的线段树,修改的时候就是把x到点分树的根路径上的所有线段树都改掉,查询同理。注意到直接这么做是会算重的,我们再开一棵以 [x的点分树子树内到fa[x]距离] 作为下标的线段树,这样修改的时候两棵都改,然后查询就能容斥去重了
线段树可以标记永久化,这样新建的节点少一些
需要注意的是点分树上两点距离是原树上两点的距离,且原树上的父子关系在点分树上不一定成立
跑得有点慢但是1A了
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
const int INF=0x3f3f3f3f;
const int N=100005;
struct treeNode {int l,r,tag;} t[N*289];
struct edge {int y,next;} e[N*2];
int dep[N],acs[N][18],fa[N],size[N],mxSon[N];
int root1[N],root2[N],tot,rec,sum;
int ls[N],edCnt,n,m;
bool del[N];
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(int x,int y) {
e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}
void dfs1(int now) {
rep(i,1,17) acs[now][i]=acs[acs[now][i-1]][i-1];
size[now]=1;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==acs[now][0]) continue;
acs[e[i].y][0]=now; dep[e[i].y]=dep[now]+1;
dfs1(e[i].y); size[now]+=size[e[i].y];
}
}
void get_root(int now,int from) {
size[now]=1; mxSon[now]=0;
for (int i=ls[now];i;i=e[i].next) {
if (e[i].y==from||del[e[i].y]) continue;
get_root(e[i].y,now); size[now]+=size[e[i].y];
mxSon[now]=std:: max(mxSon[now],size[e[i].y]);
}
mxSon[now]=std:: max(mxSon[now],sum-size[now]);
if (mxSon[now]<mxSon[rec]) rec=now;
}
void build(int now) {
for (int i=ls[now];i;i=e[i].next) {
if (del[e[i].y]) continue;
rec=0; sum=size[e[i].y];
get_root(e[i].y,now);
fa[rec]=now; del[rec]=1;
build(rec);
}
}
int get_lca(int x,int y) {
if (dep[x]<dep[y]) std:: swap(x,y);
drp(i,17,0) if (dep[acs[x][i]]>=dep[y]) x=acs[x][i];
if (x==y) return x;
drp(i,17,0) if (acs[x][i]!=acs[y][i]) x=acs[x][i],y=acs[y][i];
return acs[x][0];
}
int get_dis(int x,int y) {
return dep[x]+dep[y]-dep[get_lca(x,y)]*2;
}
void modify(int &now,int tl,int tr,int l,int r,int v) {
if (r<l) return ;
if (!now) now=++tot;
if (tl>=l&&tr<=r) return (void) (t[now].tag+=v);
int mid=(tl+tr)>>1;
modify(t[now].l,tl,mid,l,std:: min(r,mid),v);
modify(t[now].r,mid+1,tr,std:: max(mid+1,l),r,v);
}
int query(int now,int tl,int tr,int x) {
if (x<tl||x>tr) return 0;
if (tl==tr) return t[now].tag;
int mid=(tl+tr)>>1;
int res=t[now].tag;
res+=query(t[now].l,tl,mid,x);
res+=query(t[now].r,mid+1,tr,x);
return res;
}
void change(int x,int d,int w) {
int dis=0,st=x;
modify(root1[x],0,n,0,d,w);
while (fa[x]) {
dis=get_dis(st,fa[x]);
modify(root1[fa[x]],0,n,0,d-dis,w);
modify(root2[x],0,n,0,d-dis,w);
x=fa[x];
}
}
int ask(int x) {
int res=query(root1[x],0,n,0);
int dis=0,st=x;
while (fa[x]) {
dis=get_dis(st,fa[x]);
res+=query(root1[fa[x]],0,n,dis);
res-=query(root2[x],0,n,dis);
x=fa[x];
}
return res;
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
n=read(),m=read(); mxSon[0]=INF;
rep(i,2,n) add_edge(read(),read());
dfs1(dep[1]=1);
sum=n; rec=0; get_root(1,0);
del[rec]=1; build(rec);
for (char opt;m--;) {
for (opt=getchar();opt!='M'&&opt!='Q';opt=getchar());
int x=read();
if (opt=='Q') printf("%d\n", ask(x));
else {
int d=read(),w=read();
change(x,d,w);
}
}
return 0;
}