题意:
一开始被给予一棵只有一个编号为0、权值为0的根节点的树,有M个操作,每个操作为以下三种之一:
操作1:输入格式为1 i 表示给i节点加个权值为0的子节点,编号为当前最大编号+1。
操作2:输入格式为2 i a 表示给i为根的子树所有节点的权值加a。
操作3:输入格式为3 i 表示输出i节点的权值。
思路:
建立dfs离散序,化树为序列。(既先序序列)
树状数组维护序列数据。
操作1:将加的节点重置为0;
操作2:将i节点所在的序列位置到i节点所在的序列位置+i节点为根的子树大小-1进行区间加a,以为先序序列子树的是连续的一段,开始位置为根的位置。
操作3:输出结果。
代码:
#include <bits/stdc++.h> typedef long long ll; using namespace std; const ll inf=1000000007; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int cnt=2, se[100005], vs[100005], ji=1; ll bit0[100005], bit1[100005]; struct w { int k, i, a; } w[500005]; vector<int> g[100005]; void add(ll *b,int i,int v) { while(i<=cnt) { b[i]+=v; i+=i&(-i); } } ll sum(ll *b,int i) { ll s=0; while(i>0) { s+=b[i]; i-=i&(-i); } return s; } void dfs(int v) { vs[v]=ji++; se[v]=1; for(int i=0; i<g[v].size(); i++) { dfs(g[v][i]); se[v]+=se[g[v][i]]; } } int main() { int m; scanf("%d",&m); for(int i=0; i<m; i++) { scanf("%d",&w[i].k); if(w[i].k==1) { scanf("%d",&w[i].i); w[i].i++; g[w[i].i].push_back(cnt); w[i].i=cnt; cnt++; } else if(w[i].k==2) { scanf("%d %d",&w[i].i,&w[i].a); w[i].i++; } else { scanf("%d",&w[i].i); w[i].i++; } } memset(bit1,0,sizeof(bit1)); memset(bit0,0,sizeof(bit0)); dfs(1); for(int i=0; i<m; i++) { int q=w[i].k; if(q==1) { int v=w[i].i; ll pa=sum(bit0,vs[v])+sum(bit1,vs[v])*vs[v]; pa=pa-sum(bit0,vs[v]-1)-sum(bit1,vs[v]-1)*(vs[v]-1); add(bit0,vs[v],-pa); } else if(q==2) { int l=vs[w[i].i], r=l+se[w[i].i]-1; add(bit0,l,-w[i].a*(l-1)); add(bit1,l,w[i].a); add(bit0,r+1,w[i].a*r); add(bit1,r+1,-w[i].a); } else { int v=w[i].i; ll pa=sum(bit0,vs[v])+sum(bit1,vs[v])*vs[v]; pa=pa-sum(bit0,vs[v]-1)-sum(bit1,vs[v]-1)*(vs[v]-1); printf("%lld\n",pa); } } return 0; }