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
Sample Output
2
3
6
HINT
数据范围:
n , m < = 1 0 5 , ∣ w ∣ < = 1 0 4 n,m<=10^5,|w|<=10^4 n,m<=105,∣w∣<=104
注意:w不一定为正整数,因为烁烁可能把皮皮鼠吓傻了。
分析:
考虑到树上距离,又是修改,直接把点分树建出来。
对于修改一个点
x
x
x,把这个点在点分治的连通块中小于等于
l
e
n
len
len的节点修改;对于他的祖先,把除了当前连通块的其他连通块的小于
l
e
n
−
d
i
s
(
x
,
a
n
c
[
x
]
)
len-dis(x,anc[x])
len−dis(x,anc[x])的点修改。
我们可以先对所有节点加上一个数,在修改点所在连通块的点减去一个数。
先把距离排序,我们可以对每个节点开一个线段树,每次相当于区间加,这题就完了。
一些小细节:
我们可以在每个节点开两个线段树,一个是下标为到当前分治中心距离为下标,另一个是到父亲分治中心距离为下标。修改当前节点时,在当前节点全部加,在对应儿子减掉。询问就是这个节点到根路径上所有与他相关的修改的和。线段树可以使用永久标记。
代码:
/**************************************************************
Problem: 4372
User: ypxrain
Language: C++
Result: Accepted
Time:23672 ms
Memory:179664 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
const int maxn=1e5+7;
using namespace std;
int n,m,x,y,k,cnt;
int ls[maxn],f[maxn][20],dep[maxn];
char op[3];
struct edge{
int y,next;
}g[maxn*2];
struct node{
int l,r,data;
}t[maxn*120];
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
f[x][0]=fa;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
dep[y]=dep[x]+1;
dfs(y,x);
}
}
int getlca(int x,int y)
{
if (dep[x]>dep[y]) swap(x,y);
int d=dep[y]-dep[x],k=19,t=1<<k;
while (d)
{
if (d>=t) d-=t,y=f[y][k];
t/=2,k--;
}
if (x==y) return x;
k=19;
while (k>=0)
{
if (f[x][k]!=f[y][k])
{
x=f[x][k];
y=f[y][k];
}
k--;
}
return f[x][0];
}
int getdis(int x,int y)
{
return dep[x]+dep[y]-2*dep[getlca(x,y)];
}
void ins(int &p,int l,int r,int x,int y,int k)
{
if (!p) p=++cnt;
if ((l==x) && (r==y))
{
t[p].data+=k;
return;
}
int mid=(l+r)/2;
if (y<=mid) ins(t[p].l,l,mid,x,y,k);
else if (x>mid) ins(t[p].r,mid+1,r,x,y,k);
else
{
ins(t[p].l,l,mid,x,mid,k);
ins(t[p].r,mid+1,r,mid+1,y,k);
}
}
int getsum(int p,int l,int r,int x)
{
if (l==r) return t[p].data;
int mid=(l+r)/2;
if (x<=mid) return t[p].data+getsum(t[p].l,l,mid,x);
else return t[p].data+getsum(t[p].r,mid+1,r,x);
}
struct tree{
int fa[maxn],root1[maxn],root2[maxn],S[maxn];
int size[maxn],vis[maxn],len[maxn],f[maxn],root,sum;
vector <int> dis1[maxn],dis2[maxn];
queue <int> q;
void findroot(int x,int F)
{
size[x]=1;
f[x]=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((y==F) || (vis[y])) continue;
findroot(y,x);
size[x]+=size[y];
f[x]=max(f[x],size[y]);
}
f[x]=max(f[x],sum-size[x]);
if ((f[x]<f[root]) || (!root)) root=x;
}
void dfs(int x,int F)
{
size[x]=1;
dis1[root].push_back(len[x]);
if (fa[root]) dis2[root].push_back(getdis(fa[root],x));
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((vis[y]) || (y==F)) continue;
len[y]=len[x]+1;
dfs(y,x);
size[x]+=size[y];
if (x==root)
{
fa[y]=x;
q.push(y);
}
}
}
void build()
{
q.push(1);
size[1]=n;
while (!q.empty())
{
int x=q.front();
q.pop();
sum=size[x];
root=0;
findroot(x,0);
fa[root]=fa[x];
len[root]=0;
dfs(root,0);
vis[root]=1;
S[root]=dis1[root].size();
sort(dis1[root].begin(),dis1[root].end());
sort(dis2[root].begin(),dis2[root].end());
}
}
void change(int x,int len,int k)
{
int s;
s=upper_bound(dis1[x].begin(),dis1[x].end(),len)-dis1[x].begin();
if (s>=1) ins(root1[x],1,S[x],1,s,k);
for (int i=x;fa[i];i=fa[i])
{
int j=fa[i];
int d=len-getdis(x,j);
s=upper_bound(dis1[j].begin(),dis1[j].end(),d)-dis1[j].begin();
if (s>=1) ins(root1[j],1,S[j],1,s,k);
s=upper_bound(dis2[i].begin(),dis2[i].end(),d)-dis2[i].begin();
if (s>=1) ins(root2[i],1,S[i],1,s,k);
}
}
int getans(int x)
{
int s,ans;
ans=getsum(root1[x],1,S[x],1);
for (int i=x;fa[i];i=fa[i])
{
int j=fa[i];
int d=getdis(x,j);
s=lower_bound(dis1[j].begin(),dis1[j].end(),d)-dis1[j].begin()+1;
ans+=getsum(root1[j],1,S[j],s);
s=lower_bound(dis2[i].begin(),dis2[i].end(),d)-dis2[i].begin()+1;
ans-=getsum(root2[i],1,S[i],s);
}
return ans;
}
}T;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
}
T.build();
for (int i=1;i<=m;i++)
{
scanf("%s",op);
if (op[0]=='M')
{
scanf("%d%d%d",&x,&y,&k);
T.change(x,y,k);
}
else
{
scanf("%d",&x);
printf("%d\n",T.getans(x));
}
}
}