【bzoj4765】 普通计算姬

题意

  给出一棵有根树,$n$个点每个都有一个点权。$m$组操作每次可以修改一个点权或者询问编号在区间$[l,r]$的点的子树权值和的和。

Solution

  我们对节点编号分块,每一块统计该块中的节点的子树权值和的和。dfs处理出修改一个节点,需要对应修改它的祖先和它的所在的哪些块。另外再开一个树状数组,树状数组中每一个元素是对应dfs的点的权值,这样我们可以求出任意子树的权值和。

  对于询问,在中间的块直接统计。两侧零散的块在树状数组上暴力查询子树权值和。所以令块的大小$S=\sqrt{n/\log{n}}$,复杂度$O(n\sqrt{n\log{n}})$

细节

  会爆long long

代码

// bzoj4765
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define ULL unsigned long long
#define inf (1ll<<30)
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
using namespace std;

const int maxn=100010;
int head[maxn],L[maxn],R[maxn],t[maxn][350],pos[maxn],n,m,S,bn,cnt,dfn,Dargen;
LL c[maxn],d[maxn],a[maxn];
ULL s[maxn];
struct edge {int to,next;}e[maxn<<1];

int lowbit(int x) {
	return x&-x;
}
void add(int x,LL val) {
	for (int i=x;i<=n;i+=lowbit(i)) c[i]+=val;
}
LL query(int x) {
	LL res=0;
	for (int i=x;i;i-=lowbit(i)) res+=c[i];
	return res;
}
void link(int u,int v) {
	e[++cnt]=(edge){v,head[u]};head[u]=cnt;
	e[++cnt]=(edge){u,head[v]};head[v]=cnt;
}
LL dfs(int x,int fa) {
	L[x]=++dfn;d[pos[x]]++;
	LL sum=a[x];add(L[x],a[x]);
	for (int i=1;i<=bn;i++) t[x][i]=d[i];
	for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa) sum+=dfs(e[i].to,x);
	R[x]=dfn;d[pos[x]]--;s[pos[x]]+=sum;
	return sum;
}
int main() {
	scanf("%d%d",&n,&m);
	S=300;bn=n/S+(n%S!=0);
	for (int i=1;i<=n;i++) pos[i]=(i-1)/S+1;
	for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for (int u,v,i=1;i<=n;i++) {
		scanf("%d%d",&u,&v);
		if ((LL)u*v==0) Dargen=u+v;
		else link(u,v);
	}
	dfs(Dargen,0);
	for (int op,x,y,i=1;i<=m;i++) {
		scanf("%d%d%d",&op,&x,&y);
		if (op==1) {
			LL d=y-a[x];a[x]=y;
			for (int j=1;j<=bn;j++) s[j]+=d*t[x][j];
			add(L[x],d);
		}
		if (op==2) {
			ULL ans=0;
			if (pos[x]==pos[y]) for (int j=x;j<=y;j++) ans+=query(R[j])-query(L[j]-1);
			else {
				for (int j=pos[x]+1;j<pos[y];j++) ans+=s[j];
				for (int j=x;j<=S*pos[x];j++) ans+=query(R[j])-query(L[j]-1);
				for (int j=(pos[y]-1)*S+1;j<=y;j++) ans+=query(R[j])-query(L[j]-1);
			}
			printf("%llu\n",ans);
		}
	}
	return 0;
}

 

posted @ 2017-03-07 20:15  MashiroSky  阅读(348)  评论(0编辑  收藏  举报