bzoj 4034: [HAOI2015]T2

Description

 有一棵点数为 N 的树,以点 1 为根,且树点有边权。然后有 M 个

操作,分为三种:
操作 1 :把某个节点 x 的点权增加 a 。
操作 2 :把某个节点 x 为根的子树中所有点的点权都增加 a 。
操作 3 :询问某个节点 x 到根的路径中所有点的点权和。

Input

 第一行包含两个整数 N, M 。表示点数和操作数。

接下来一行 N 个整数,表示树中节点的初始权值。
接下来 N-1 行每行三个正整数 fr, to , 表示该树中存在一条边 (fr, to) 。
再接下来 M 行,每行分别表示一次操作。其中第一个数表示该操
作的种类( 1-3 ) ,之后接这个操作的参数( x 或者 x a ) 。

Output

 对于每个询问操作,输出该询问的答案。答案之间用换行隔开。

Sample Input

5 5
1 2 3 4 5
1 2
1 4
2 3
2 5
3 3
1 2 1
3 5
2 1 2
3 3

Sample Output

6
9
13

HINT

 对于 100% 的数据, N,M<=100000 ,且所有输入数据的绝对值都不


会超过 10^6 。

Source

鸣谢bhiaibogf提供


一开始想用树链剖分,结果发现只需要维护dfs序就可以了

我用的是线段树维护。。没去想怎么用树状数组维护

首先dfs记录进入的时刻和弹出的时刻。进入权值为正,弹出为负

然后要查询路径的话直接查询1到x的权值和就可以了

单点修改就直接线段树上修改

子树就该即修改进入和弹出中间的那一段

每段维护正权值点和以及负权值点和。最后减一下就可以了

*记得开long long

#include<cstdio>
using namespace std;
struct line
{
     int s,t;
     int next;
}a[200001];
int head[100001];
int edge;
inline void add(int s,int t)
{
	 a[edge].next=head[s];
     head[s]=edge;
     a[edge].s=s;
     a[edge].t=t;
}
int w[200001],b[200001];
int ld[200001],rd[200001];
bool v[200001];
int tot;
struct tree
{
     int l,r;
     long long s1,s2;
     long long x1,x2;
     long long tag;
}tr[800001];
inline void up(int p)
{
     tr[p].s1=tr[p*2].s1+tr[p*2+1].s1;
     tr[p].x1=tr[p*2].x1+tr[p*2+1].x1;
     tr[p].s2=tr[p*2].s2+tr[p*2+1].s2;
     tr[p].x2=tr[p*2].x2+tr[p*2+1].x2;
}
inline void down(int p)
{
     long long ll1,ll2,lr1,lr2;
     long long x=tr[p].tag;
     ll1=tr[p*2].x1;
     ll2=tr[p*2].x2;
     lr1=tr[p*2+1].x1;
     lr2=tr[p*2+1].x2;
     tr[p*2].s1+=ll1*x;
     tr[p*2].s2+=ll2*x;
     tr[p*2+1].s1+=lr1*x;
     tr[p*2+1].s2+=lr2*x;
     tr[p*2].tag+=x;
     tr[p*2+1].tag+=x;
     tr[p].tag=0;
}
inline void build(int p,int l,int r)
{
     tr[p].l=l;
     tr[p].r=r;
     if(l!=r)
     {
          int mid=(l+r)/2;
          build(p*2,l,mid);
          build(p*2+1,mid+1,r);
          up(p);
     }
     else
     {
     	  if(w[l]>0)
     	  {
               tr[p].s1=b[w[l]];
               tr[p].x1=1;
          }
          else
          {
               tr[p].s2=b[-w[l]];
               tr[p].x2=1;
          }
     }
}
inline void add1(int p,int l,int r,long long x)
{
     if(l<=tr[p].l&&tr[p].r<=r)
     {
          if(tr[p].x1==1)
               tr[p].s1+=x;
          else
               tr[p].s2+=x;
     }
     else
     {
     	  down(p);
          int mid=(tr[p].l+tr[p].r)/2;
          if(l<=mid)
               add1(p*2,l,r,x);
          if(r>mid)
               add1(p*2+1,l,r,x);
          up(p);
     }
}
inline void add2(int p,int l,int r,long long x)
{
     if(l<=tr[p].l&&tr[p].r<=r)
     {
          long long l1=tr[p].x1,l2=tr[p].x2;
          tr[p].s1+=x*l1;
          tr[p].s2+=x*l2;
          tr[p].tag+=x;
     }
     else
     {
     	  down(p);
          int mid=(tr[p].l+tr[p].r)/2;
          if(l<=mid)
               add2(p*2,l,r,x);
          if(r>mid)
               add2(p*2+1,l,r,x);
           up(p);
     }
}
tree nw;
inline tree ask(int p,int l,int r)
{
     if(l<=tr[p].l&&tr[p].r<=r)
          return tr[p];
     else
     {
     	  down(p);
          int mid=(tr[p].l+tr[p].r)/2;
          tree ans1=nw,ans2=nw,ans=nw;
          bool flag1=false,flag2=false;
          if(l<=mid)
          {
               flag1=true;
               ans1=ask(p*2,l,r);
          }
          if(r>mid)
          {
               flag2=true;
               ans2=ask(p*2+1,l,r);
          }
          if(flag1)
          {
               if(flag2)
               {
                    ans.s1=ans1.s1+ans2.s1;
                    ans.s2=ans1.s2+ans2.s2;
                    ans.x1=ans1.x1+ans2.x1;
                    ans.x2=ans1.x2+ans2.x2;
               }
               else
                    ans=ans1;
          }
          else
               ans=ans2;
          return ans;
     }
}
inline void dfs(int d)
{
	 tot++;
	 ld[d]=tot;
	 w[tot]=d;
	 v[d]=true;
     int i;
     for(i=head[d];i!=0;i=a[i].next)
     {
          int t=a[i].t;
          if(!v[t])
               dfs(t);
     }
     tot++;
     rd[d]=tot;
	 w[tot]=-d;
}
int main()
{
     int n,m;
     scanf("%d%d",&n,&m);
     int i;
     for(i=1;i<=n;i++)
          scanf("%d",&b[i]);
     int s,t;
     for(i=1;i<=n-1;i++)
     {
          scanf("%d%d",&s,&t);
          edge++;
          add(s,t);
          edge++;
          add(t,s);
     }
     dfs(1);
     build(1,1,tot);
     int x;
     for(i=1;i<=m;i++)
     {
          scanf("%d",&x);
          if(x==1)
          {
               scanf("%d%d",&s,&t);
               add1(1,ld[s],ld[s],t);
               add1(1,rd[s],rd[s],t);
          }
          else if(x==2)
          {
               scanf("%d%d",&s,&t);
               add2(1,ld[s],rd[s],t);
          }
          else
          {
               scanf("%d",&s);
               tree xt=ask(1,1,ld[s]);
               printf("%lld\n",xt.s1-xt.s2);
          }
     }
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值