HDU 6962 I love tree (线段树 + 树链剖分)

链接:I love tree

题意:

给一棵树 , 初始所有点的点权为 0 ,现有两种操作 :

  1. 给出 a , b,把a 到 b路径上的点依次加上 1 ^ 2 , 2 ^ 2 , 3 ^ 2。
  2. 给出一个 a 求点 a 的权值。

思路:

  1. 如果是一个普通的数组进行上述修改操作 , 我们有两种方法 , 观察上述操作,发现加上的值差分后是一个等差数列 , 所以我们把原数组差分,把操作数也差分一下,就变成区间维护一个等差数列,一个点的答案就是前缀和。但是这是在树上 , 感觉就不太好操作了,多校的题解说是要分块。
  2. 另外一种做法就是把加的平方数变成点编号的平方,相当于做一个区间 + 1 , 最后算答案的时候用这个值乘编号的平方 。 举个例子 如果当前操作区间为 [l , r] , 区间中当前点编号为 id , 那么这个点加的就是 (id - (l - 1) ) ^ 2 ,展开后发现需要加的 id ^ 2之和当前点的编号有关 , 所以这里可以用区间 + 1 维护。- 2 * id * (l - 1)所以就是区间加 (l - 1),最后的值乘 -2 * id。这里的(l - 1)对整个要加区间都是定值。所以最后开三个线段树维护这三个值就好了。
  3. 最后还要注意对于一条链 (a , b)正着加和反着加是不一样的。还有在树上对一条链加的时候会被分成log条小链,而这些小链的编号不一定是连续的 , 所以在加的时候要看当前已经加了多少个点了,上述的 l 时会有变化。

代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <math.h>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e6 + 7;
ll sum[4][maxn << 2],lazy[4][maxn << 2],val[maxn];
int n;
int depth[maxn],siz[maxn],fa[maxn],son[maxn],cnt,id[maxn],tot[maxn],num,head[maxn<<1];
struct node{
     int u,v,next;
}e[maxn<<1];
void add(int u,int v){
     e[++num].next=head[u];
     e[num].v=v;
     head[u]=num;
}
void dfs1(int u,int pre,int dep){
     depth[u]=dep; siz[u]=1; fa[u]=pre;

     for(int i=head[u]; i; i=e[i].next){
        int to = e[i].v;
        if( to == pre) continue;
        dfs1(to,u,dep+1);
        siz[u]+=siz[to];
        if(siz[to]>siz[son[u]]) son[u]=to;  //更新重儿子

     }
}
void dfs2(int u,int t){
    id[u]=++cnt;               //赋予新的序列号
    tot[u]=t;                  //重边顶点
    if(son[u]) dfs2(son[u],t); //优先对重链编号
    for(int i=head[u];i;i=e[i].next){
        int to=e[i].v;
        if(to!=son[u]&&to!=fa[u])  //跳过重儿子和父节点
            dfs2(to,to);       //非重儿子的 tot是本身
    }
}
int lca(int x,int y){
    while(tot[x]!=tot[y]){
        if(depth[tot[x]]<depth[tot[y]]) swap(x,y);
        x=fa[tot[x]];
    }
    if(depth[x]>depth[y]) return y;
    return x;
}
void pushdown(int id , int rt, int l, int r){
    int m = (l + r) >> 1;
    if(lazy[id][rt]){
        lazy[id][rt << 1] += lazy[id][rt];
        lazy[id][rt << 1 | 1] += lazy[id][rt];
        sum[id][rt << 1] += lazy[id][rt] * (m - l + 1);
        sum[id][rt << 1 | 1] += lazy[id][rt] * (r - m);
        lazy[id][rt]=0;
    }
}
void update(int id , int L, int R, ll v, int l, int r, int rt){
     if(L <= l && R >= r){
        sum[id][rt] += v * (r - l + 1);
        lazy[id][rt] += v;
        return ;
     }
     pushdown(id , rt , l , r);
     int m = (l + r) >> 1;
     if(L <= m) update(id , L , R , v , l , m , rt << 1);
     if(R > m) update(id , L , R , v , m + 1, r , rt << 1 | 1);
     sum[id][rt] = sum[id][rt << 1] + sum[id][rt << 1 | 1];
}
ll query(int id , int pos , int l,int r,int rt){
     ll s = 0;
     if(l == r){
        return sum[id][rt];
     }
     pushdown(id , rt , l , r);
     int m = (l + r) >> 1;
     if(pos <= m) s += query(id , pos , l , m , rt << 1);
     if(pos > m)  s += query(id , pos , m + 1 , r , rt << 1 | 1);
     return s;
}

void add_v(int x,int y , int len){
     int l = 1 , r = len;
     while(tot[x] != tot[y]){
         if(depth [tot[x]] > depth[tot[y]]){
             ll val = id[tot[x]] - l;
             update(1 , id[tot[x]] , id[x] , 1 , 1 , n , 1);
             update(2 , id[tot[x]] , id[x] , val , 1 , n , 1);
             update(3 , id[tot[x]] , id[x] , val * val ,1 , n , 1);
             l += depth[x] - depth[tot[x]] + 1;
             x = fa [tot[x]];
         }
         else{
              ll val = id[tot[y]] + r;
              update(1 , id[tot[y]] , id[y] , 1 , 1 , n , 1);
              update(2 , id[tot[y]] , id[y] , val , 1 , n , 1);
              update(3 , id[tot[y]] , id[y] , val * val ,1 , n , 1);
              r -= depth[y] - depth[tot[y]] + 1;
              y = fa [tot[y]];

         }
     }
     if(depth[x] < depth[y]){
         ll val = id[x] - l;
         update(1 , id[x] , id[y] , 1 , 1 , n , 1);
         update(2 , id[x] , id[y] , val , 1 , n , 1);
         update(3 , id[x] , id[y] , val * val ,1 ,  n , 1);
     }
     else{
        ll val = id[y] + r;
        update(1 , id[y] , id[x] , 1 , 1 , n , 1);
        update(2 , id[y] , id[x] , val , 1 , n , 1);
        update(3 , id[y] , id[x] , val * val ,1 ,  n , 1);
     }
}
int main() {
    scanf("%d",&n);
    for(int i = 1 , u , v; i < n; i ++){
        scanf("%d%d",&u,&v);
        add(u , v);
        add(v , u);
    }
    dfs1(1, -1, 0);
    dfs2(1 , 1);
    int q;
    scanf("%d",&q);
    int op , l , r;
    while(q--){
        scanf("%d",&op);
        if(op == 1){
            scanf("%d%d",&l,&r);
            int lc = lca(l , r);
            add_v(l , r , depth[l] + depth[r] - 2 * depth[lc] + 1);
        }
        else{
            scanf("%d",&l);
            l = id[l];
            ll ans1 = query(1 , l , 1 , n , 1),ans2 = query(2 , l , 1 , n , 1),ans3 = query(3 , l , 1 , n , 1);
            printf ("%lld\n",ans1 * l * l - 2 * ans2 * l + ans3);

        }
    }
    return 0;
}
/*
5
1 2
1 3
3 4
3 5
1000
1 4 5
2 5
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值