本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
"奋战三星期,造台计算机"。小G响应号召,花了三小时造了台普通计算姬。普通计算姬比普通计算机要厉害一些
。普通计算机能计算数列区间和,而普通计算姬能计算树中子树和。更具体地,小G的计算姬可以解决这么个问题
:给定一棵n个节点的带权树,节点编号为1到n,以root为根,设sum[p]表示以点p为根的这棵子树中所有节点的权
值和。计算姬支持下列两种操作:
1 给定两个整数u,v,修改点u的权值为v。
2 给定两个整数l,r,计算sum[l]+sum[l+1]+....+sum[r-1]+sum[r]
尽管计算姬可以很快完成这个问题,可是小G并不知道它的答案是否正确,你能帮助他吗?
Input
第一行两个整数n,m,表示树的节点数与操作次数。
接下来一行n个整数,第i个整数di表示点i的初始权值。
接下来n行每行两个整数ai,bi,表示一条树上的边,若ai=0则说明bi是根。
接下来m行每行三个整数,第一个整数op表示操作类型。
若op=1则接下来两个整数u,v表示将点u的权值修改为v。
若op=2则接下来两个整数l,r表示询问。
N<=10^5,M<=10^5
0<=Di,V<2^31,1<=L<=R<=N,1<=U<=N
Output
对每个操作类型2输出一行一个整数表示答案。
Sample Input
6 4
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5
0 0 3 4 0 1
0 1
1 2
2 3
2 4
3 5
5 6
2 1 2
1 1 1
2 3 6
2 3 5
Sample Output
16
10
9
10
9
正解:分块
解题报告:
解题报告:
这道题折腾了我一个上午…
我开始傻傻地写了一个带$log$的算法,在$BZOJ$老爷机上死活跑不过去…
考虑把$1$到$n$分块,那么每次对于整块我直接调用答案,对于剩余的部分我暴力做,一个一个在$dfs$序上查询就可以了。
那么我们如何来处理整块的答案呢,我开始的做法很辣鸡,就是对于块内每个点$dfs$序区间打上一个$+1$的标记,然后每个块都要对$1$到$n$求一遍$1$到$n$对这个块的贡献,这样的话就带了个$log$,跑得很慢。
实际上我只需要在做每个块时,$dfs$一遍整棵树,顺便给每个点打个标记,就可以直接做完了不需要$log$…
这样我就可以$n\sqrt{n}$地预处理出每个点对每个块的答案。
那么修改操作只需要根据我要修改的点对所有块的影响,修改一下每个块的$sum$即可。
那么问题只剩下了如何处理快速查询$dfs$序上区间和了。
我开始无脑上树状数组,发现尽管树状数组常数小,但是终究是多了一个$log$...
仔细思考发现,对于这个操作只需要支持单点修改和区间查询,树状数组是$O(logn)-O(logn)$的,而我的修改因为第一步对整块的修改已经是根号级别了,那此处的$log$未免显得浪费了,而查询的时候又会多一个$log$,所以我们考虑能否把查询变为$O(1)$。
不难发现我只需要对于$dfs$序区间另外分块,就可以做到$O(sqrt(n))-O(1)$的了,然后这道题就做完了...
这道题启示窝,要不断思考如何平衡不同操作之间的复杂度和常数优化...
还有$unsigned$ $long$ $long$!!!
//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#define lc root<<1
#define rc root<<1|1
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int MAXN = 100011;
const int MAXM = 200011;
const int MAXS = 2017;
int n,m,rt,ecnt,first[MAXN],to[MAXM],next[MAXM],wei[MAXN];
int dfn[MAXN],pre[MAXN],last[MAXN],belong[MAXN],block,kcnt,L[MAXS],R[MAXS];
bool in[MAXN];
LL S[MAXN][320],val[MAXN],a[MAXN],qian[MAXN];
ULL sum[MAXS],ans,A;
namespace FK{
LL c[MAXS],ss[MAXN];
inline void add(int x,LL val){
int bel=belong[x];
for(int i=x;i<=R[bel];i++) ss[i]+=val;
for(int i=bel;i<=kcnt;i++) c[i]+=val;
}
inline LL Q(int l,int r){//查询dfs序上的区间[l,r]
LL tot=0; int ll,rr; ll=belong[l]; rr=belong[r];
if(ll==rr) {
if(l==L[ll]) tot=ss[r];
else tot=ss[r]-ss[l-1];
return tot;
}
if(l!=L[ll]) {
tot+=ss[L[ll+1]-1]-ss[l-1];
ll++;
}
if(r!=R[rr]) {
tot+=ss[r];
rr--;
}
if(ll<=rr) tot+=c[rr]-c[ll-1];
return tot;
}
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void dfs(int x,int fa){
dfn[x]=++ecnt; pre[ecnt]=x;
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
last[x]=ecnt;
}
inline void dfs2(int x,int fa,int lei,int K){
if(in[x]) lei++;
S[x][K]=lei;
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs2(v,x,lei,K);
}
}
inline void build(){
ecnt=0; dfs(rt,0);
for(int i=1;i<=n;i++) qian[i]=qian[i-1]+a[pre[i]];
//block=3;
block=330;
for(int i=1;i<=n;i++) belong[i]=(i-1)/block+1;
kcnt=n/block; if(n%block) kcnt++;
for(int i=1;i<=kcnt+1;i++) L[i]=n+1;
for(int i=1;i<=n;i++) L[belong[i]]=min(L[belong[i]],i),R[belong[i]]=i;
for(int i=1;i<=kcnt;i++) {
FK::ss[ L[i] ]=a[pre[ L[i] ]];
FK::c[i]=FK::c[i-1]+a[pre[ L[i] ]];
for(int j=L[i]+1;j<=R[i];j++) {
FK::c[i]+=a[ pre[j] ];
FK::ss[j]=FK::ss[j-1]+a[ pre[j] ];
}
}
for(int i=1;i<=kcnt;++i) {
for(int j=L[i];j<=R[i];++j) {
in[j]=1;
sum[i]+=qian[last[j]]-qian[dfn[j]-1];
}
dfs2(rt,0,0,i);
for(int j=L[i];j<=R[i];++j) in[j]=0;
}
//cout<<clock()<<endl;
}
inline void work(){
//printf("%d\n",sizeof(S)/1024/1024);
n=getint(); m=getint(); int type,x,y;
for(int i=1;i<=n;i++) a[i]=getint();
for(int i=1;i<=n;i++) {
x=getint(); y=getint(); if(x>y) swap(x,y);
if(x==0) { rt=y; continue; }
link(x,y); link(y,x);
}
build();
int ll,rr; LL cha;
while(m--) {
type=getint(); x=getint(); y=getint();
if(type==1) {
cha=y-a[x];
FK::add(dfn[x],y-a[x]);//单点修改...
//还需要修改对每个块的贡献...
for(int i=1;i<=kcnt;++i)
sum[i]+=cha*S[x][i];
a[x]=y;
}
else {
ans=0;
if(belong[x]==belong[y] || belong[x]==belong[y]-1){
for(int i=x;i<=y;++i)
ans+=FK::Q(dfn[i],last[i]);
printf("%llu\n",ans);
continue;
}
if(x==L[ belong[x] ]) ll=belong[x];
else ll=belong[x]+1;
if(y==R[ belong[y] ]) rr=belong[y];
else rr=belong[y]-1;
for(int i=ll;i<=rr;++i)
ans+=sum[i];
for(int i=x;i<L[ll];++i)
ans+=FK::Q(dfn[i],last[i]);
for(int i=R[rr]+1;i<=y;++i)
ans+=FK::Q(dfn[i],last[i]);
printf("%llu\n",ans);
}
}
//cout<<endl<<clock()<<endl;
}
int main()
{
work();
return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。