bzoj 4765: 普通计算姬

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

Sample Output

16
10
9

HINT

 

 

思路

分块分块再分块,

假设块的长度是m,

可以先对编号分块,预处理出每个点对每一块的贡献(即这一块中有多少个x的祖先)。

这样时间复杂度n^2/m,空间复杂度n^2/m,为了不mle,m适宜<500。

这样修改操作就可以O(n/m)完成。

考虑询问操作,中间的完整块很好处理O(n/m),但左右端的残余块难处理。

可以扫残余块中的每个点,计算它们子树和的和,大约要计算m次子树和,即查询m次。

考虑怎样很快的计算子树和。

一种是log的做法,dfs序建树状数组,修改O(logn),查询O(logn)。但是这样查询多了一个log,不优。

考虑,每次操作,修改只进行了1次,查询进行了m次。所以考虑使修改变慢,查询变快。

所以,再对dfs序进行分块即可。这样修改O(n/m),查询1次O(1),非常优越。

时间复杂度n^2/m+nm=n*(n/m+m)。所以当m=sqrt(n)的时候,时间复杂度为O(n sqrt(n))

由于这种做法常数较大,虽然是O(n sqrt(n))的,有可能比那些带log的程序要慢(这也很尴尬啊)

 

代码

  1 #include <cmath>
  2 #include <cstdio>
  3 #include <iostream>
  4 using namespace std;
  5 int read()
  6 {
  7     char c=getchar();int d=0,f=1;
  8     for(;c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
  9     for(;c>='0'&&c<='9';d=d*10+c-48,c=getchar());
 10     return d*f;
 11 }
 12 typedef unsigned long long ull;
 13 typedef long long ll;
 14 const int N=100002;
 15 int M,n,m,cnt,tot;
 16 int tmp[N],fa[N],bl[N],L[N],R[N],w[N*2],head[N],size[N],pos[N],t[320][N];
 17 ll s[N*2],bs[N*2],a[N*2];
 18 struct xint{int to,next;}e[N*2];
 19 inline void addedge(int x,int y)
 20 {
 21     e[++cnt]=(xint){y,head[x]};
 22     head[x]=cnt;
 23 }
 24 inline void add(int x,int s)
 25 {
 26     for(int i=R[x];i<=R[n];i+=M)a[i]+=s;
 27     for(int i=x;i<=R[x]-1;++i)a[i]+=s; 
 28 }
 29 inline ll ask(int x)
 30 {
 31     int A=pos[x]+size[x]-1,B=pos[x]-1;
 32     return ((A%M)?a[L[A]-1]+a[A]:a[A])-((B%M)?a[L[B]-1]+a[B]:a[B]);
 33 }
 34 inline void dfs(int x)
 35 {
 36     tmp[bl[x]]++;
 37     for(int i=1;i<=bl[n];++i)t[i][x]=tmp[i];
 38     pos[x]=++tot;
 39     size[x]=1; s[x]=w[x];
 40     for(int i=head[x];i;i=e[i].next)
 41     {
 42         int y=e[i].to;
 43         if(y==fa[x])continue;
 44         fa[y]=x;dfs(y);
 45         size[x]+=size[y];
 46         s[x]+=s[y];
 47     }
 48     tmp[bl[x]]--;
 49 }
 50 inline void init()
 51 {
 52     n=read()+1,m=read();
 53     M=int(sqrt(n));
 54     for(int i=1;i<=n;++i)bl[i]=(i-1)/M+1;
 55     for(int i=1;i<=n;++i)L[i]=bl[i]*M-M+1,R[i]=bl[i]*M;
 56     for(int i=2;i<=n;++i)w[i]=read();
 57     for(int i=2;i<=n;++i)
 58     {
 59         int x=read()+1,y=read()+1;
 60         addedge(x,y);
 61         addedge(y,x);
 62     }
 63     dfs(1);
 64     for(int i=1;i<=n;++i)bs[bl[i]]+=s[i];
 65     for(int i=1;i<=n;++i)add(pos[i],w[i]);
 66 }
 67 inline ull solve(int x,int y)
 68 {
 69     ull res=0;
 70     if(bl[x]==bl[y])
 71     {
 72         for(int i=x;i<=y;++i)res+=ask(i);
 73         return res;
 74     }
 75     for(int i=x;i<=R[x];++i)res+=ask(i);
 76     for(int i=L[y];i<=y;++i)res+=ask(i);
 77     for(int i=bl[x]+1;i<=bl[y]-1;++i)res+=bs[i];
 78     return res;
 79 }
 80 inline void work()
 81 {
 82     while(m--)
 83     {
 84         int o=read(),x=read(),y=read();
 85         if(o==1)
 86         {
 87             x++;
 88             ll dw=y-w[x];
 89             for(int i=1;i<=bl[n];++i)
 90                 bs[i]+=dw*t[i][x];
 91             add(pos[x],dw);
 92             w[x]=y;
 93         }else
 94         if(o==2)
 95         {
 96             x++,y++;
 97             ull ans=solve(x,y);
 98             printf("%llu\n",ans);
 99         }
100     }
101 }
102 int main()
103 {
104     init();
105     work();
106 }

 

转载于:https://www.cnblogs.com/Blog-of-Eden/p/6685423.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值