BZOJ 4765: 普通计算姬 [分块 树状数组 DFS序]

时间:2023-03-08 18:36:50
BZOJ 4765: 普通计算姬 [分块 树状数组 DFS序]

传送门

题意:

一棵树,支持单点修改和询问以$[l,r]$为根的子树的权值和的和


只有我这种不会分块的沙茶不会做这道题吗?

说一点总结:

子树和当然上$dfs$序了,询问原序列一段区间所有子树和,对原序列分块,$sum_i$为一块的答案

查询很显然了,整块用$sum$,非整块暴力查子树

修改的话,预处理$f[i][j]$为点$j$对第$i$块的贡献,一遍$dfs$就可以预处理出来

然后,我的$BIT$用了$build$函数竟然比不用还慢

真的很好写

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+,M=;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,Q,u,v,op,w[N];
int root;
struct Edge{
int v,ne;
}e[N<<];
int cnt,h[N];
inline void ins(int u,int v){
cnt++;
e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
cnt++;
e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
}
struct BIT{
ll c[N];
inline void add(int p,ll v){for(;p<=n;p+=(p&-p)) c[p]+=v;}
inline ll sum(int p){
ll re=;
for(;p;p-=(p&-p)) re+=c[p];
return re;
}
}C;
int block,m,pos[N];
int a[N],L[N],R[N],dfc;
int f[M][N];
void dfs(int u,int fa){
L[u]=++dfc;
a[pos[u]]++;
for(int i=;i<=m;i++) f[i][u]+=a[i]; for(int i=h[u];i;i=e[i].ne)
if(e[i].v!=fa) dfs(e[i].v,u); R[u]=dfc;
a[pos[u]]--;
}
ll subtree(int x){return C.sum(R[x])-C.sum(L[x]-);}
ll sum[N];
void Change(int x,int v){
C.add(L[x],v-w[x]);
for(int i=;i<=m;i++) sum[i]+=(ll)(v-w[x])*f[i][x];
w[x]=v;
}
ll Query(int l,int r){
ll re=;
if(pos[l]==pos[r])
for(int i=l;i<=r;i++) re+=subtree(i);
else{
int _=pos[l]*block;
for(int i=l;i<=_;i++) re+=subtree(i);
for(int i=(pos[r]-)*block+;i<=r;i++) re+=subtree(i);
for(int i=pos[l]+;i<pos[r];i++) re+=sum[i];
}
return re;
}
int main(){
freopen("in","r",stdin);
n=read();Q=read();
block=sqrt(n);
m=(n-)/block+;
for(int i=;i<=n;i++) w[i]=read();
for(int i=;i<=n;i++){
u=read();v=read();
if(u) ins(u,v); else root=v;
pos[i]=(i-)/block+;
}
dfs(root,);
for(int i=;i<=n;i++) C.add(L[i],w[i]);
for(int i=;i<=m;i++){
int l=(i-)*block+,r=min(i*block,n);
for(int j=l;j<=r;j++) sum[i]+=subtree(j);
}
while(Q--){
op=read();u=read();v=read();
if(op==) Change(u,v);
else printf("%llu\n",Query(u,v));
}
}

MashiroSky怎么这么快啊.....发现他一开始子树和写在dfs里我也改成那样啦怎么还是这么慢><.......

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+,M=;
inline int read(){
char c=getchar();int x=,f=;
while(c<''||c>''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
}
int n,Q,u,v,op,w[N];
int root;
struct Edge{
int v,ne;
}e[N<<];
int cnt,h[N];
inline void ins(int u,int v){
cnt++;
e[cnt].v=v;e[cnt].ne=h[u];h[u]=cnt;
cnt++;
e[cnt].v=u;e[cnt].ne=h[v];h[v]=cnt;
}
struct BIT{
ll c[N];
inline void add(int p,ll v){for(;p<=n;p+=(p&-p)) c[p]+=v;}
inline ll sum(int p){
ll re=;
for(;p;p-=(p&-p)) re+=c[p];
return re;
}
}C;
int block,m,pos[N];
int a[N],L[N],R[N],dfc;
int f[M][N];
ll subtree(int x){return C.sum(R[x])-C.sum(L[x]-);}
ll sum[N];
ll dfs(int u,int fa){
L[u]=++dfc; a[pos[u]]++;
C.add(L[u],w[u]);
ll s=w[u];
for(int i=;i<=m;i++) f[i][u]+=a[i];
for(int i=h[u];i;i=e[i].ne)
if(e[i].v!=fa) s+=dfs(e[i].v,u);
R[u]=dfc; a[pos[u]]--;
sum[pos[u]]+=s;
return s;
}
void Change(int x,int v){
C.add(L[x],v-w[x]);
for(int i=;i<=m;i++) sum[i]+=(ll)(v-w[x])*f[i][x];
w[x]=v;
}
ll Query(int l,int r){
ll re=;
if(pos[l]==pos[r])
for(int i=l;i<=r;i++) re+=subtree(i);
else{
int _=pos[l]*block;
for(int i=l;i<=_;i++) re+=subtree(i);
for(int i=(pos[r]-)*block+;i<=r;i++) re+=subtree(i);
for(int i=pos[l]+;i<pos[r];i++) re+=sum[i];
}
return re;
}
int main(){
freopen("in","r",stdin);
n=read();Q=read();
block=;
m=(n-)/block+;
for(int i=;i<=n;i++) w[i]=read();
for(int i=;i<=n;i++){
u=read();v=read();
if(u) ins(u,v); else root=v;
pos[i]=(i-)/block+;
}
dfs(root,);
while(Q--){
op=read();u=read();v=read();
if(op==) Change(u,v);
else printf("%llu\n",Query(u,v));
}
}