bzoj 4765: 普通计算姬 分块

来源:互联网 发布:怎样退出淘宝账号 编辑:程序博客网 时间:2024/04/30 02:01

题意

给定一棵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]
N<=10^5,M<=10^5

分析

既然不能用数据结构来维护,那就只能上暴力分块了。
一开始的想法是对sum数组分块,那么我们可以先用差分预处理出s[i,j]表示第i块中dfs序的第j个数的系数,ans[i]表示第i块的答案。那么修改就可以在O(n)内完成。查询的话,中间的块扫一遍,多出来的单独在dfs序内查找即可。
对于dfs序,我们可以用树状数组来维护,那么修改的复杂度就是O(logn),查询的总复杂度就变成了O(nlogn)

据说这样是可以卡过去的。
但我们有更优的办法。

注意到修改的复杂度远远低于查询的复杂度,那么我们考虑是否能够均衡这两个操作的复杂度呢?答案是肯定的。
我们可以对dfs序进行分块,处理的是前缀和,那么修改操作就变成了O(n),查询单个子树的复杂度就变成了O(1),那么总复杂度就变成了O(nn)了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<cmath>using namespace std;typedef unsigned long long LL;const int N=100005;const int B=420;int n,m,cnt,last[N],pos[N],tim,mn[N],mx[N],sum[B][N],bel[N],block,sta[B],end[B],root,t[N];struct edge{int to,next;}e[N*2];LL ans[B],dfn[N],tag[B],a[N];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*10+ch-'0';ch=getchar();}    return x*f;}void addedge(int u,int v){    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;    e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;}void dfs(int x,int fa){    mn[x]=++tim;pos[tim]=x;    for (int i=last[x];i;i=e[i].next)    {        if (e[i].to==fa) continue;        dfs(e[i].to,x);    }    mx[x]=tim;}void prework(){    dfs(root,0);    for (int i=1;i<=n;i++) dfn[i]=dfn[i-1]+a[pos[i]];    for (int i=1;i<=bel[n];i++)    {        memset(t,0,sizeof(t));        for (int j=sta[i];j<=end[i];j++) t[mn[j]]++,t[mx[j]+1]--;        for (int j=1;j<=n;j++) sum[i][j]=sum[i][j-1]+t[j],ans[i]+=(LL)sum[i][j]*a[pos[j]];    }}void modify(int x,LL y){    LL delta=y-a[x];a[x]=y;    for (int i=1;i<=bel[n];i++) ans[i]+=(LL)delta*sum[i][mn[x]];    for (int i=bel[mn[x]]+1;i<=bel[n];i++) tag[i]+=delta;    for (int i=mn[x];i<=end[bel[mn[x]]];i++) dfn[i]+=delta;}LL find(int l,int r){    return dfn[r]+tag[bel[r]]-dfn[l-1]-tag[bel[l-1]];}LL query(int l,int r){    LL tot=0;    if (bel[l]==bel[r])    {        for (int i=l;i<=r;i++) tot+=find(mn[i],mx[i]);        return tot;    }    for (int i=bel[l]+1;i<bel[r];i++) tot+=ans[i];    for (int i=l;i<=end[bel[l]];i++) tot+=find(mn[i],mx[i]);    for (int i=sta[bel[r]];i<=r;i++) tot+=find(mn[i],mx[i]);    return tot;}int main(){    n=read();m=read();    block=sqrt(n);    for (int i=1;i<=n;i++)    {        a[i]=read();        bel[i]=(i+block-1)/block;        if (!sta[bel[i]]) sta[bel[i]]=i;        end[bel[i]]=i;    }    for (int i=1;i<=n;i++)    {        int x=read(),y=read();        if (!x) root=y;        else addedge(x,y);    }    prework();    while (m--)    {        int op=read(),x=read();LL y=read();        if (op==1) modify(x,y);        else printf("%llu\n",query(x,y));    }    return 0;}
0 0