[BZOJ4372]烁烁的游戏
Description
背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠。
题意:
给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠。
烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠。皮皮鼠会被烁烁吸引,所以会一直待在节点上不动。
烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠。
大意:
给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:
Q x:询问x的点权。
M x d w:将树上与节点x距离不超过d的节点的点权均加上w。
Input
第一行两个正整数:n,m
接下来的n-1行,每行三个正整数u,v,代表u,v之间有一条边。
接下来的m行,每行给出上述两种操作中的一种。
Output
对于每个Q操作,输出当前x节点的皮皮鼠数量。
Sample Input
7 6
1 2
1 4
1 5
2 3
2 7
5 6
M 1 1 2
Q 5
M 2 2 3
Q 3
M 1 2 1
Q 2
1 2
1 4
1 5
2 3
2 7
5 6
M 1 1 2
Q 5
M 2 2 3
Q 3
M 1 2 1
Q 2
Sample Output
2
3
6
3
6
HINT
数据范围:
n,m<=10^5,|w|<=10^4
注意:w不一定为正整数,因为烁烁可能把皮皮鼠吓傻了。
链剖$LCA$打挂调一上午身败名裂
其实这道题的思路根震波那道题是很相似的,建立点分树然后用线段树维护
不过这里是区间修改和单点查询,而那道是单点修改区间查询
维护两颗线段树分别表示点分树某重心子树内到它的距离等于$d$的点和子树内到他父亲距离等于$d$的点
然后查询的时候跳点分树$fa$,和之前的点容斥后统计答案即可
这样为什么是对的,因为原树上任意两点经过的第一个重心为他们在点分树上的$LCA$
于是如果一个点$y$的修改能对$x$产生影响,只有在他们的$LCA$处才会对答案产生贡献
$LCA$再往上会被它的儿子容斥掉
解释得不是很清楚自己意会吧
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define M 100010 #define ls ch[node][0] #define rs ch[node][1] using namespace std; int read() { char ch=getchar();int x=0; while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x; } int n,m,num,cnt,root,S; int sz[M],size[M],top[M],fa[M],deep[M],son[M],head[M],maxn[M],f[M]; int rt1[M],rt2[M],val[M<<8],ch[M<<8][2];bool vis[M]; struct point{int next,to;}e[M<<1]; void add(int from,int to) { e[++num].next=head[from]; e[num].to=to; head[from]=num; } void dfs1(int x) { sz[x]=1;deep[x]=deep[fa[x]]+1; for(int i=head[x];i;i=e[i].next) { int to=e[i].to; if(fa[x]==to) continue; fa[to]=x,dfs1(to),sz[x]+=sz[to]; if(sz[to]>sz[son[x]]) son[x]=to; } } void dfs2(int x,int tp) { top[x]=tp; if(son[x]) dfs2(son[x],tp); for(int i=head[x];i;i=e[i].next) if(e[i].to!=son[x]&&e[i].to!=fa[x]) dfs2(e[i].to,e[i].to); } int lca(int x,int y) { while(top[x]!=top[y]) { if(deep[top[x]]<deep[top[y]]) swap(x,y); x=fa[top[x]]; } return deep[x]>deep[y]?y:x; } int dis(int x,int y) { return deep[x]+deep[y]-2*deep[lca(x,y)]; } void getroot(int x,int fa) { size[x]=1;maxn[x]=0; for(int i=head[x];i;i=e[i].next) { int to=e[i].to; if(to==fa||vis[to]) continue; getroot(to,x); size[x]+=size[to]; maxn[x]=max(maxn[x],size[to]); } maxn[x]=max(maxn[x],S-size[x]); if(maxn[x]<maxn[root]) root=x; } void solve(int x,int fa) { vis[x]=true;f[x]=fa; for(int i=head[x];i;i=e[i].next) { int to=e[i].to; if(vis[to]) continue; root=0,S=size[to],getroot(to,0); solve(root,x); } } void change(int &node,int l,int r,int l1,int r1,int v) { if(!node) node=++cnt; if(l1<=l&&r1>=r) { val[node]+=v;return; } int mid=(l+r)/2; if(l1<=mid) change(ls,l,mid,l1,r1,v); if(r1>mid) change(rs,mid+1,r,l1,r1,v); } int query(int node,int l,int r,int x) { if(!node) return 0; if(l==r) return val[node]; int mid=(l+r)/2; if(x<=mid) return val[node]+query(ls,l,mid,x); else return val[node]+query(rs,mid+1,r,x); } void update(int x,int d,int v) { change(rt1[x],0,n,0,d,v); for(int i=x;f[i];i=f[i]) { int dt=dis(x,f[i]); if(dt>d) continue; change(rt1[f[i]],0,n,0,d-dt,v); change(rt2[i],0,n,0,d-dt,v); } } int ask(int x) { int res=query(rt1[x],0,n,0); for(int i=x;f[i];i=f[i]) { int dt=dis(x,f[i]); res+=query(rt1[f[i]],0,n,dt)-query(rt2[i],0,n,dt); } return res; } int main() { n=read();m=read(); for(int i=1;i<n;i++) { int u=read(),v=read(); add(u,v),add(v,u); } dfs1(1),dfs2(1,1); maxn[0]=S=n; getroot(1,0),solve(root,0); for(int i=1;i<=m;i++) { char s[10];scanf("%s",s); if(s[0]=='M') { int x=read(),d=read(),w=read(); update(x,d,w); } else { int x=read(); printf("%d\n",ask(x)); } } return 0; }