bzoj4034 [HAOI2015]T2

  首先求出树的dfs序,对于第一种操作和第二种操作分开处理,第一种操作比较经典,就不说了。第二种操作其实和第一种也很类似,查询x点到根的权值和sum(x)=(disx+1)*v(x)-w,其中disx为x点到根的距离,v(x)表示以x到根之间的点为第二类操作的子树根所进行操作的权值和,这样相乘明显会有多余的部分,假设对y点进行第二类操作,使其和其子树上的点增加z,那么会多计算价值disy*z,使用两颗树状数组维护即可。

  代码

  

 1 #include<cstdio>
 2 #define nc getchar
 3 #define N 1000010
 4 int tot,n,m,i,v[N],dp,p[N],pre[N],tt[N],L[N],R[N],a,b,T;
 5 long long c[N][3],dis[N],ans;
 6 inline int lowbit(int x)
 7 {
 8     return x&(-x);
 9 }
10 void cc(int x,long long w,int typ)
11 {
12     while (x<=tot)
13     {
14         c[x][typ]+=w;
15         x+=lowbit(x);
16     }
17 }
18 long long sum(int x,int typ)
19 {
20     long long ans=0;
21     while (x)
22     {
23         ans+=c[x][typ];
24         x-=lowbit(x);
25     }
26     return ans; 
27 }
28 void link(int x,int y)
29 {
30     dp++;pre[dp]=p[x];p[x]=dp;tt[dp]=y;
31 }
32 void dfs(int x,int fa)
33 {
34     int i=p[x];
35     tot++;L[x]=tot;
36     while (i)
37     {
38         if (tt[i]!=fa)
39         {
40             dis[tt[i]]=dis[x]+1;
41             dfs(tt[i],x);
42         }
43         i=pre[i];
44     }
45     tot++;R[x]=tot;
46 }
47 int main()
48 {
49     scanf("%d%d",&n,&m);
50     for (i=1;i<=n;i++)
51     scanf("%d",&v[i]);
52     for (i=1;i<n;i++)
53     {
54         scanf("%d%d",&a,&b);
55         link(a,b);link(b,a);
56     }
57     dfs(1,0);
58     for (i=1;i<=n;i++)
59     {
60         cc(L[i],v[i],0);
61         cc(R[i],-v[i],0);
62     }
63     for (i=1;i<=m;i++)
64     {
65         scanf("%d",&T);
66         if (T==1)
67         {
68             scanf("%d%d",&a,&b);
69             cc(L[a],b,0);
70             cc(R[a],-b,0);
71         }
72         else
73         if (T==2)
74         {
75             scanf("%d%d",&a,&b);
76             cc(L[a],b,1);
77             cc(R[a],-b,1);
78             cc(L[a],dis[a]*(long long)b,2);
79             cc(R[a],-dis[a]*(long long)b,2);
80         }
81         else
82         {
83             scanf("%d",&a);
84             ans=sum(L[a],0)-sum(L[a],2)+sum(L[a],1)*(dis[a]+1);
85             printf("%lld\n",ans);
86         }
87     }
88     
89 }

 

转载于:https://www.cnblogs.com/fzmh/p/5392429.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值