YZOJ P3484 子树求和
时间限制:2000MS 内存限制:262144KB 出题人:lgj
难度: \(6.0\)
-
题目描述
已知一棵树有 \(n\) 个节点,并且根节点是固定的。
每个节点上都有一个权值 \(w_i\) ,记 \(s_i\) 为 以 \(i\) 为根的子树中,所有节点的 \(w_i\) 的和。
由于询问 \(s_i\) 太简单了,不能将 AKIOI
的你的高智商体现出来,所以每次询问给定 \(l, r\) ,求 \(\sum\limits_{i=l}^{r}{s_i}\) 。
为了避免此题难度太低,不能将 AKIOI
的你的高智商体现出来,所以的询问的过程中还可能修改某个节点的 \(w_i\) 。
为了将 AKIOI
的你的高智商体现出来,你要写一个程序来实时给出询问的答案。
-
输入格式
第一行为两个整数 \(n\) 和 \(q\),分别表示节点数和操作的次数;
第二行 \(n\) 个正整数,表示序列 \(w\) ;
接下来 \(n\) 行,第 \(i\) 行两个正整数 \(u_i\) 和 \(v_i\),描述一条树上的边。特别地,\(u_i=0\) 时,表示 \(v_i\) 为树的根节点;
接下来 \(q\) 行,每行三个正整数 \(op, l, r\) 。描述 \(q\) 组操作。 \(op=1\) 表示 \(w_l\) 修改为 \(r\),\(op=2\) 表示询问 \(\sum\limits_{i=l}^{r}{s_i}\) 的值。
-
输出格式
对于每组询问操作,你需要依据当前树的情况输出该组询问的标准答案,每次询问的答案独占一行。
-
样例输入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
6 6 7 4 3 4 9 1 4 2 0 1 2 1 2 3 5 3 6 5 2 1 3 1 1 1 2 3 6 2 3 5 1 3 5 2 6 6 |
-
样例输出
1 2 3 4 |
62 28 27 1 |
(假样例)
-
数据规模与约定
对于 \(40\%\) 的数据,\(n, q \leq 3000\) ;
对于另外 \(15\%\) 的数据,所有 \(op=2\) 的操作 \(l=r\) ;
对于 \(100\%\) 的数据,\(n, q \leq 100000\),\( w_i \leq 10^9\) 。
首先把每个节点的 dfn 求出来,那么一个节点的子树就对于其中连续的一段区间。差分预处理修改每个点会对其他节点造成多少的影响,分块维护 dfn 序列,由于查询比较多(每次查询一个编号都会对应查询这里的一段区间),所以前缀和维护分块。同时分块维护编号序列,由于每次只会修改一个编号,所以使用普通的分块即可。
修改的时间复杂度:编号 \(O(1)\),dfn \(O(\sqrt n)\) ;
查询的时间复杂度:编号 \(O(\sqrt n)\) , dfn \(O(1)\) ;
总时间复杂度 \(O(n\sqrt n)\) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #define _min(_a_,_b_) ((_a_)<(_b_)?(_a_):(_b_)) #ifdef ONLINE_JUDGE char __B[1<<15],*__S=__B,*__T=__B; #define getchar() (__S==__T&&(__T=(__S=__B)+fread(__B,1,1<<15,stdin),__S==__T)?EOF:*__S++) #endif inline int getnum() { register char c=getchar(); while(!(c>='0' && c<='9')) c=getchar(); register int a=0; while(c>='0' && c<='9') { a*=10;a+=c-'0'; c=getchar(); } return a; } struct _node { int o; int next; }graph[105050]; int gcnt; _node glist[205050]; inline void insertLine(const int&s,const int&t) { _node*n=&graph[s]; _node*nw=&glist[++gcnt]; nw->o=t,nw->next=n->next; n->next=gcnt; } int w[105050];long long s[105050]; int size[105050],dfn[105050],rdfn[105050],ldfn; void DFS(register int o,register int father) { dfn[++ldfn]=o,rdfn[o]=ldfn; s[o]+=w[o],size[o]++; _node*n=&graph[o]; while(n->next) { n=&glist[n->next]; if(n->o!=father) { DFS(n->o,o); s[o]+=s[n->o],size[o]+=size[n->o]; } } } int N; int BlockSize,BlockNum; long long BlockPSumW[350],BlockSumS[350]; long long BlockPW[105050]; //int ContWtoS[105050][350]; int ContWtoS[350][105050]; #define GetBlockPos(_p_) (((_p_)-1)/BlockSize+1) #define GetBlockLeft(_w_) (((_w_)-1)*BlockSize+1) #define GetBlockRight(_w_) _min((_w_)*BlockSize,N) inline void InitializeBlocks() { BlockSize=__builtin_sqrt(N),BlockNum=N/BlockSize+(N%BlockSize!=0); for(register int i=1;i<=N;i++) { BlockSumS[GetBlockPos(i)]+=s[i]; if(GetBlockPos(i) == GetBlockPos(i-1)) BlockPW[i]=BlockPW[i-1]+w[dfn[i]]; else BlockPW[i]=w[dfn[i]]; } for(register int i=1;i<=BlockNum;i++) BlockPSumW[i]=BlockPSumW[i-1]+BlockPW[GetBlockRight(i)]; for(register int si=1;si<=BlockNum;si++) { register int left=GetBlockLeft(si),right=GetBlockRight(si); for(register int i=left;i<=right;i++) ContWtoS[si][rdfn[i]]++,ContWtoS[si][rdfn[i]+size[i]]--; for(register int i=1;i<=N;i++) ContWtoS[si][i]+=ContWtoS[si][i-1]; } } inline void EditBlockW(const int&pos,const int&val) { register int bi=GetBlockPos(pos),right=GetBlockRight(bi); //printf("editW: %d(%d) %d\n",pos,bi,val); for(register int i=pos;i<=right;i++) BlockPW[i]+=val; for(register int i=bi;i<=BlockNum;i++) BlockPSumW[i]=BlockPSumW[i-1]+BlockPW[GetBlockRight(i)]; for(register int i=1;i<=BlockNum;i++) BlockSumS[i]+=(long long)ContWtoS[i][pos]*val; } inline long long QueryBlockSumW(const int&lpos,const int&rpos) { register int bl=GetBlockPos(lpos),br=GetBlockPos(rpos); //printf("queryW: %d(%d) %d(%d)\n",lpos,bl,rpos,br); long long ans=BlockPSumW[br]-BlockPSumW[bl-1]; if(lpos != GetBlockLeft(bl)) ans-=BlockPW[lpos-1]; if(rpos != GetBlockRight(br)) ans-=(BlockPW[GetBlockRight(br)]-BlockPW[rpos]); return ans; } inline long long QueryBlockSumS(const int&lpos,const int&rpos) { register int bl=GetBlockPos(lpos),br=GetBlockPos(rpos); //printf("queryS: %d(%d) %d(%d)\n",lpos,bl,rpos,br); long long ans=0; for(register int i=bl;i<=br;i++) ans+=BlockSumS[i]; for(register int i=GetBlockLeft(bl);i<=lpos-1;i++) ans-=QueryBlockSumW(rdfn[i],rdfn[i]+size[i]-1); register int right=GetBlockRight(br); for(register int i=rpos+1;i<=right;i++) ans-=QueryBlockSumW(rdfn[i],rdfn[i]+size[i]-1); return ans; } int main() { N=getnum();register int Q=getnum(); for(register int i=1;i<=N;i++) w[i]=getnum(); register int root=0; for(register int i=1;i<=N;i++) { register int u=getnum(),v=getnum(); if(!u) root=v; else insertLine(u,v),insertLine(v,u); } DFS(root,0); InitializeBlocks(); for(register int lp=1;lp<=Q;lp++) { register int op=getnum(),l=getnum(),r=getnum(); if(op==2) printf("%lld\n",QueryBlockSumS(l,r)); else EditBlockW(rdfn[l],r-w[l]),w[l]=r; } return 0; } |
怎么咕了
口胡完成(