树链剖分 ---- 2021杭电多校 1002 I love tree[详解]

题目链接


题目大意:

就是给你一颗树
树上每个点的初始权值为 0 0 0
现在有两个操作

  1. < 1 , a , b > <1,a,b> <1,a,b>对于 a a a b b b路径上所有点加上到 a a a的距离的平方和
  2. < 2 , a > <2,a> <2,a>询问 a a a点的权值

解题思路:

  1. 对于树链上的修改问题我们很容易想到树链剖分

  2. 但是怎么对于每个点距离 a a a的距离不一样那么累加的权值也不一样怎么办?

  3. 首先我们知道对于树链剖分上面的线段树是用 时 间 戳 d f n 时间戳dfn dfn进行区间划分的,那么我们就可以往 d f n dfn dfn上面想

  4. 我们观察一下下面的图片就是 < a , b > <a,b> <a,b>的路径上面有两种情况 a − > l c a a->lca a>lca l c a − > b lca->b lca>b

  5. 现在假设我们要得到某个点得值那么只能是靠 d f n [ x ] dfn[x] dfn[x]去计算答案
    d i s t a n c e ( a , x ) 2 = ( d f n [ x ] − ? ) 2 distance(a,x)^2=(dfn[x]-?)^2 distance(a,x)2=(dfn[x]?)2

  6. 现在就是要求这个问号是什么?
    对于 a − > l c a a->lca a>lca的情况

  7. 我们知道对于 [ d f n [ t o p [ x ] ] , d f n [ x ] ] [dfn[top[x]],dfn[x]] [dfn[top[x]],dfn[x]]这是一段连续的区间

  8. 对于里面的任意一个点 x ′ x' x我们可以 ( d f n [ x ′ ] − d f n [ x ] ) 2 (dfn[x']-dfn[x])^2 (dfn[x]dfn[x])2
    在这里插入图片描述

  9. 但是我们知道树链剖分是一段一段的跳的那么上面的那个式子还得加上 a 和 x 之 间 有 多 少 个 点 a和x之间有多少个点 ax

  10. 但是注意 ( d f n [ x ′ ] − d f n [ x ] ) 2 (dfn[x']-dfn[x])^2 (dfn[x]dfn[x])2里面是负数所以你要减掉点数

  11. ( d f n [ x ′ ] − d f n [ x ] − p o i n t ′ s    n u m b r e    b e t w e e n    x    a n d    a ) 2 (dfn[x']-dfn[x]-point's \; numbre\;between\;x\;and\;a)^2 (dfn[x]dfn[x]pointsnumbrebetweenxanda)2

  12. ( d f n [ x ′ ] − ( d f n [ x ] + p o i n t ′ s    n u m b r e    b e t w e e n    x    a n d    a ) ) 2 (dfn[x']-(dfn[x]+point's \; numbre\;between\;x\;and\;a))^2 (dfn[x](dfn[x]+pointsnumbrebetweenxanda))2

  13. n u m = ( d f n [ x ] + p o i n t ′ s    n u m b r e    b e t w e e n    x    a n d    a ) num=(dfn[x]+point's \; numbre\;between\;x\;and\;a) num=(dfn[x]+pointsnumbrebetweenxanda)

  14. ( d f n [ x ′ ] − n u m ) 2 = d f n [ x ′ ] 2 − 2 ∗ n u m ∗ d f n [ x ′ ] + n u m 2 (dfn[x']-num)^2=dfn[x']^2-2*num*dfn[x']+num^2 (dfn[x]num)2=dfn[x]22numdfn[x]+num2

  15. 那么我们就可以用3个线段树去维护这3项的系数 1 , 2 ∗ n u m , n u m 2 1,2*num,num^2 1,2num,num2因为对于同一个区间 n u m num num
    是一样的
    在这里插入图片描述
    对于 b − > l c a b->lca b>lca的情况

  16. 这里是 ( d f n [ x ′ ] − d f n [ t o p [ x ′ ] ] ) 2 (dfn[x']-dfn[top[x']])^2 (dfn[x]dfn[top[x]])2注意这里面 d f n [ x ′ ] − d f n [ t o p [ x ′ ] ] dfn[x']-dfn[top[x']] dfn[x]dfn[top[x]]是大于0的

  17. 那么和上面同理但是计算距离的时候你要算出 t o p [ x ′ ] 到 a 的 距 离 top[x']到a的距离 top[x]a


AC代码:

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
   read(first);
   read(args...);
}
int T;
int n, m;
struct node {
   int to, next;
}e[N];
int head[N], cnt;
inline void add(int from, int to) {
   e[cnt] = {to,head[from]};
   head[from] = cnt ++;
}
//...............................
int fa[N], dep[N], siz[N], son[N];
inline void dfs1(int u, int f) {
   fa[u] = f, dep[u] = dep[f] + 1, siz[u] = 1;
   int max_siz = -1;
   for(int i = head[u]; ~i; i = e[i].next) {
      int v = e[i].to;
      if(v == f) continue;
      dfs1(v,u);
      siz[u] += siz[v];
      if(siz[v] > max_siz) {
         son[u] = v;
         max_siz = siz[v];
      } 
   }
}

ll tim, dfn[N], top[N];
inline void dfs2(int u, int t) {
   dfn[u] = ++ tim, top[u] = t;
   if(!son[u]) return;
    dfs2(son[u],t);
    for(int i = head[u]; ~i; i = e[i].next) {
       int v = e[i].to;
       if(v == fa[u] || v == son[u]) continue;
       dfs2(v,v);
    }
}
struct Segtree {
   ll tr[maxn], lazy[maxn];
   void add(int rt , int l, int r, int posL, int posR, ll val) {
      if(posL <= l && posR >= r) {
         tr[rt] += val;
         lazy[rt] += val;
         return;
      }
      if(posL <= mid) add(Lson,posL,posR,val);
      if(posR > mid) add(Rson,posL,posR,val);
   }
   inline void pushdown(int rt) {
      if(lazy[rt]) {
         lazy[rt << 1] += lazy[rt];
         lazy[rt << 1|1] += lazy[rt];
         tr[rt << 1] += lazy[rt];
         tr[rt << 1|1] += lazy[rt];
         lazy[rt] = 0;
      }
   }
   ll ask(int rt, int l, int r, int pos) {
      if(l == r) return tr[rt];
      pushdown(rt);
      if(pos <= mid) return ask(Lson,pos);
      else return ask(Rson,pos);
   }
}seg[3];

int LCA(int x,int y)
{
    for(;top[x]!=top[y];dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]);
    return dep[x]<dep[y]?x:y;
}

inline void tree_change(int a, int b) {
   int lca = LCA(a,b);
   int len = dep[a] + dep[b] - 2 * dep[lca] + 1;
   int up = 1, down = 0;
   // cout << top[a] << " " << top[b] <<endl;
   while(top[a] != top[b]) {
      if(dep[top[a]] > dep[top[b]]) {
          ll k = dfn[a] + up;
         //  cout << k << endl;
          seg[0].add(1,1,n,dfn[top[a]],dfn[a],k*k);
          seg[1].add(1,1,n,dfn[top[a]],dfn[a],2*k);
          seg[2].add(1,1,n,dfn[top[a]],dfn[a],1);
          up += dep[a] - dep[top[a]] + 1;
          a = fa[top[a]];
      } else {
          ll k = dfn[top[b]] - 1 - (len - (dep[b] - dep[top[b]] + 1) - down);//后面计算距离
          seg[0].add(1,1,n,dfn[top[b]],dfn[b],k*k);
          seg[1].add(1,1,n,dfn[top[b]],dfn[b],2*k);
          seg[2].add(1,1,n,dfn[top[b]],dfn[b],1);
          down += dep[b] - dep[top[b]] + 1;
          b = fa[top[b]];
      }
   }
   if(dep[a] > dep[b]) {
         ll k = dfn[a] + up;
         seg[0].add(1,1,n,dfn[b],dfn[a],k*k);
         seg[1].add(1,1,n,dfn[b],dfn[a],2*k);
         seg[2].add(1,1,n,dfn[b],dfn[a],1);      
   } else {
          
          ll k = dfn[a] - 1 - (len - (dep[b] - dep[a] + 1) - down);
         //  cout << k <<endl;
          seg[0].add(1,1,n,dfn[a],dfn[b],k*k);
          seg[1].add(1,1,n,dfn[a],dfn[b],2*k);
          seg[2].add(1,1,n,dfn[a],dfn[b],1);    
   }
}

inline ll ask(int pos) {
   return 1ll * dfn[pos] * dfn[pos] * seg[2].ask(1,1,n,dfn[pos]) - 1ll * dfn[pos] * seg[1].ask(1,1,n,dfn[pos]) + 1ll * seg[0].ask(1,1,n,dfn[pos]);
}

int main() {
    //IOS;
    ms(head,-1);
    cin >> n;
    for(int i = 1; i < n; ++ i) {
       int u, v;
       cin >> u >> v;
       add(u,v);
       add(v,u);
    }
    dfs1(1,0);
    dfs2(1,0);
   //  for(int i = 1; i <= n; ++ i) cout << dfn[i] << endl;
    int m;
    cin >> m;
    while(m --) {
       int op;
       cin >> op;
       if(op == 1) {
          int a, b;
          cin >> a >> b;
          tree_change(a,b);
       } else {
          int pos;
          cin >> pos;
          cout << ask(pos) << endl;
       }
    }
    return 0;
}
/*

9
2 1
3 1
4 2
5 2
6 4
7 3
8 3
9 3
2
1 8 4
2 1

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

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

*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值