Description
请你维护这样一棵树,它的每个结点上有一个权值,它支持两种操作:
1、Q x:询问以x结点为根的子树的所有结点的权值和;
2、C x y:将以x结点为根的子树的所有结点的权值加上y。
树的结点以1~n进行标号(n<=100000),初始时所有结点的权值为0。
Input
第一行有两个数字n,m。m表示询问的个数(m<=100000),n如题目所述。
接下来n-1行,每行两个整数x,y(1<=x,y<=n),表示x结点与y结点连接。
再接下来有m行,每行表示一个询问,询问格式如题目所述。
n,m<=500000,保证答案以及所有中间过程中的数值均在长整型范围内
Output
包含若干行,对应着每个询问的答案。
Sample Input
5 3
1 2
1 3
2 4
2 5
Q 1
C 1 4
Q 1
Sample Output
0
20
Sol
愉悦身心QAQ
学数学心态爆炸之后放松心情写的~
Code
#include <bits/stdc++.h>
#define mid ((s[x].l+s[x].r)>>1)
using namespace std;
int n,m,x,y,in[1000005],out[1000005],I;char op[5];vector<int>e[1000005];
void dfs(int x,int fa){in[x]=++I;for(int i=0;i<e[x].size();i++) if(e[x][i]!=fa) dfs(e[x][i],x);out[x]=I;}
struct seg{long long l,r,v,z;}s[10000005];
void down(int x)
{
s[x*2].v+=s[x].z*(s[x*2].r-s[x*2].l+1);s[x*2+1].v+=(s[x].z*(s[x*2+1].r-s[x*2+1].l+1));
s[x*2].z+=s[x].z;s[x*2+1].z+=s[x].z;s[x].z=0;
}
void build(int x,int L,int R){s[x]=(seg){L,R,0,0};if(L==R) return;build(x*2,L,mid),build(x*2+1,mid+1,R);}
long long query(int x,int L,int R)
{
down(x);
if(s[x].l>=L&&s[x].r<=R) return s[x].v;return (L<=mid?query(x*2,L,R):0)+(R>mid?query(x*2+1,L,R):0);
}
long long update(int x,int L,int R,long long V)
{
down(x);
if(s[x].l>=L&&s[x].r<=R) return s[x].v=s[x].v+(s[x].z=(s[x].z+V))*(s[x].r-s[x].l+1);
return s[x].v=(L<=mid?update(x*2,L,R,V):s[x*2].v)+(R>mid?update(x*2+1,L,R,V):s[x*2+1].v);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),e[x].push_back(y),e[y].push_back(x);
dfs(1,0);build(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%s",op);
if(op[0]=='Q') scanf("%d",&x),printf("%lld\n",query(1,in[x],out[x]));
if(op[0]=='C') scanf("%d%d",&x,&y),update(1,in[x],out[x],y);
}
}