Codevs 4633 [Mz]树链剖分练习

4633 [Mz]树链剖分练习

时间限制: 1 s    空间限制: 64000 KB    题目等级 : 大师 Master

题目描述 Description

给定一棵结点数为n的树,初始点权均为0,有依次q个操作,每次操作有三个参数a,b,c,当a=1时,表示给b号结点到c号结点路径上的所有点(包括b,c,下同)权值都增加1,当a=2时,表示询问b号结点到c号结点路径上的所有点权值之和。

输入描述 Input Description

第一行,一个正整数n。

接下来n-1行,每行一对正整数x,y,表示x号结点和y号结点之间有一条边。

第n+1行,一个正整数q。

最后q行,每行一组正整数a,b,c,表示操作的三个参数。b和c可能相等。

保证数据都是合法的。

输出描述 Output Description

若干行,每行一个非负整数表示答案。

样例输入 Sample Input

5

1 2

2 3

1 4

2 5

5

1 4 5

2 1 5

1 1 3

2 5 3

2 4 3

 

样例输出 Sample Output

3

4

6

 

数据范围及提示 Data Size & Hint

共有10个测试点,对于第i个测试点,当1<=i<=4时,n=q=10^i,当5<=i<=10时,n=q=10000*i。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 using namespace std;
  5 #define maxn 10000*10
  6 struct Edge{ int to,next,value; }e[maxn*2+10];
  7 struct Node{ int l,r,sum,lazy; }tre[maxn*5];
  8 int dep[maxn],pos[maxn],size[maxn],son[maxn],fa[maxn];
  9 int top[maxn],head[maxn],tot=0,n,visx;
 10 void Add_Edge(int u,int v){
 11     e[++tot].to=v;e[tot].next=head[u];
 12     head[u]=tot;
 13 }
 14 void DFS_First(int now,int pre,int deepth){
 15     fa[now]=pre;size[now]=1;dep[now]=deepth;
 16     for(int i=head[now];i;i=e[i].next){
 17         int v=e[i].to;
 18         if(v!=pre&&!dep[v]){
 19             DFS_First(v,now,deepth+1);
 20             size[now]+=size[v];
 21             if(!son[now]||size[son[now]]<size[v])
 22                 son[now]=v;
 23         }
 24     }
 25 }
 26 void DFS_Second(int now,int Top){
 27     pos[now]=++visx;top[now]=Top;
 28     if(son[now])DFS_Second(son[now],Top);
 29     for(int i=head[now];i;i=e[i].next){
 30         int v=e[i].to;
 31         if(v!=son[now]&&v!=fa[now])DFS_Second(v,v);
 32     }
 33 }
 34 void Build(int now,int l,int r){
 35     tre[now].l=l;tre[now].r=r;
 36     if(l==r)return;
 37     int mid=(l+r)>>1;
 38     Build(now<<1,l,mid);Build(now<<1|1,mid+1,r);
 39 }
 40 void down(int now){
 41     int k=tre[now].lazy;
 42     tre[now<<1].lazy+=k;tre[now<<1|1].lazy+=k;
 43     tre[now].lazy=0;
 44     tre[now<<1].sum+=k*(tre[now<<1].r-tre[now<<1].l+1);
 45     tre[now<<1|1].sum+=k*(tre[now<<1|1].r-tre[now<<1|1].l+1);
 46 }
 47 int query(int now,int l,int r){
 48     if(l<=tre[now].l&&tre[now].r<=r)return tre[now].sum;
 49     if(tre[now].lazy)down(now);
 50     int ans=0,mid=(tre[now].l+tre[now].r)>>1;
 51     if(mid>=l)ans+=query(now<<1,l,r);
 52     if(mid<r)ans+=query(now<<1|1,l,r);
 53     tre[now].sum=tre[now<<1].sum+tre[now<<1|1].sum;
 54     return ans;
 55 }
 56 int QuerySum(int u,int v){
 57     int ans=0;
 58     while(top[u]!=top[v]){
 59         if(dep[top[u]] < dep[top[v]])swap(u,v);
 60         ans+=query(1,pos[top[u]],pos[u]);
 61         u=fa[top[u]];
 62     }
 63     if(dep[u]>dep[v])swap(u,v);
 64     ans+=query(1,pos[u],pos[v]);
 65     return ans;
 66 }
 67 void UpDate(int now,int l,int r){
 68     if(l<=tre[now].l&&tre[now].r<=r){
 69         tre[now].lazy++;
 70         tre[now].sum+=tre[now].r-tre[now].l+1;
 71         return ;
 72     }
 73     if(tre[now].lazy)down(now);
 74     int mid=(tre[now].l+tre[now].r)/2;
 75     if(mid>=l)UpDate(now<<1,l,r);
 76     if(mid<r)UpDate(now<<1|1,l,r);
 77     tre[now].sum=tre[now<<1].sum+tre[now<<1|1].sum;
 78     return ;
 79 }
 80 void Change(int u,int v){
 81     while(top[u]!=top[v]){
 82         if(dep[top[u]]<dep[top[v]])swap(u,v);
 83         UpDate(1,pos[top[u]],pos[u]);
 84         u=fa[top[u]];
 85     }
 86     if(dep[u]>dep[v])swap(u,v);
 87     UpDate(1,pos[u],pos[v]);
 88 }
 89 int main(){
 90     scanf("%d",&n);
 91     int q,x,y,z;
 92     for(int i=1,u,v;i<n;i++){
 93         scanf("%d%d",&u,&v);
 94         Add_Edge(u,v);Add_Edge(v,u);
 95     }
 96     DFS_First(1,0,0);
 97     DFS_Second(1,1);
 98     Build(1,1,n);
 99     
100     scanf("%d",&q);
101     while(q--){
102         scanf("%d%d%d",&x,&y,&z);
103         if(x==1)Change(y,z);
104         else printf("%d\n",QuerySum(y,z));
105     }
106     return 0;
107 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七情六欲·

学生党不容易~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值