Problem:
F : Tree / 木

問題文

大きさ N の根付き木がある。 各頂点には 0 から N−1 までの番号が付けられ、根が 0である。 任意の辺の両端の頂点について、割り当てられた整数は根に近いほうが小さい。初めは全ての辺の重みは 0 である。

この木に対する Q 個のクエリを順に処理せよ。クエリには以下の 2 種類がある。

頂点 u, v の距離(パスに含まれる辺の重みの和)を求める。

頂点 v の子孫(自身を含まない)に接続する辺の重みを全て x 増加させる。

入力

入力は以下の形式からなる。

N Q
a_0 b_0
…
a_{N−2} b_{N−2}
q_0
…
q_{Q−1}

a_i,b_i は i 番目の辺が結ぶ頂点である。 q_i は i 番目のクエリを表す 3 つの整数であり、次のうちいずれかである。

0 \ u_i \ v_i : 頂点 u_i, v_i の距離を求める。

1 \ v_i \ x_i : 頂点 v_i の子孫に接続する辺の重みを全て x_i 増加させる。

制約

入力はすべて整数である

2 \≤ N \≤ 150\,000

1 \≤ Q \≤ 150\,000

0 \≤ a_{i},b_{i},u_{i},v_{i} \≤ N−1

a_{i} < b_{i}

0 \≤ x_{i} \≤ 300

出力

距離を求めるクエリのたびに、結果を1行で出力せよ。

注意

この問題では入出力が非常に大きくなりうるので高速な関数を使用してほしい。

サンプル

サンプル入力1

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

サンプル出力1

8
5

図のようになる。

サンプル入力2

13 7
0 1
1 2
2 3
1 4
3 5
1 6
2 7
0 8
7 9
5 10
8 11
1 12
1 2 143
0 8 7
0 10 6
1 1 42
1 6 37
0 3 6
1 6 38

サンプル出力2

143
429
269

Solution in C++11 (judge verdict: Accepted)

#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)(n);i++)
#define all(a) (a).begin(), (a).end()
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;

inline int MSB(int x){ return x>0?31-__builtin_clz(x):-1; }

struct SparseTable{
  vi val;
  vector<vi> table;

  SparseTable(void){}
  SparseTable(const vi a):val(a){
    int n = a.size(), ln = MSB(n);
    table = vector<vi>(n,vi(ln+1,0));
    rep(i,n)table[i][0] = i;

    int k = 1;
    rep(j,ln){
      rep(i,n){
	int id1 = table[i][j], id2 = (i+k<n)?table[i+k][j]:id1;
	table[i][j+1] = (val[id1]<=val[id2])?id1:id2;
      }
      k <<= 1;
    }
  }

  inline int rmqi(int l, int r){
    int ln = MSB(r-l);
    int id1 = table[l][ln], id2 = table[r-(1<<ln)][ln];
    return (val[id1]<=val[id2])?id1:id2;
  }

  inline int rmq(int l, int r){ return val[rmqi(l,r)]; }
};

struct dif1RMQ{
  int s;
  vi val, subval, subid, bit;
  SparseTable st;
  vector< vector<vi> > lookup_table;

  dif1RMQ(void){}
  dif1RMQ(const vi a):val(a){
    int n = a.size();
    s = max(1,MSB(n)/2);
    int m = (n+s-1)/s;

    subval.resize(m); subid.resize(m); bit.resize(m);
    rep(i,m){
      subval[i] = val[s*i]; subid[i] = s*i;
      int lim = min(s*(i+1),n);
      for(int j=s*i;j<lim;j++){
	if(subval[i] > val[j]){ subval[i] = val[j]; subid[i] = j; }
      }
      bit[i] = 0;
      for(int j=s*i;j<lim-1;j++){
	if(val[j]<val[j+1])bit[i] |= 1<<(j-s*i+1);
      }
    }
    st = SparseTable(subval);

    lookup_table.resize(1<<s);
    rep(i,1<<s)lookup_table[i] = vector<vi>(s, vi(s,0));
      
    rep(i,1<<s)rep(j,s){
      int cur = 0, minv = 0, minid = j;
      lookup_table[i][j][j] = j;
      for(int k=j+1;k<s;k++){
	if( (i>>k)&1 )cur++;
	else cur--;
	if(minv > cur){ minv = cur; minid = k; }
	lookup_table[i][j][k] = minid;
      }
    }
  }

  inline int rmqi(int l, int r){
    r--;
    int bl = l/s, br = r/s;
    if(bl == br)return lookup_table[bit[bl]][l-bl*s][r-br*s] + bl*s;
    else{
      int res = lookup_table[bit[bl]][l-bl*s][s-1] + bl*s;
      int tmp = lookup_table[bit[br]][0][r-br*s] + br*s;
      if(val[res] > val[tmp])res = tmp;

      if(br-bl>1){
	tmp = subid[st.rmqi(bl+1,br)];
	if(val[res] > val[tmp])res = tmp;
      }
      return res;
    }
  }

  inline int rmq(int l, int r){ return val[rmqi(l,r)]; }
};

struct LCA{
  vi tour, depth, id;
  dif1RMQ dr;
  
  LCA(void){}
  LCA(const vector<vi> t,int root){
    int n = t.size(), k = 0;
    tour.resize(2*n-1); depth.resize(2*n-1); id.resize(n);
    dfs(root,-1,0,k,t);
    dr = dif1RMQ(depth);
  }

  inline void dfs(int v,int p, int d, int &k, const vector<vi> &t){
    id[v] = k; tour[k] = v;
    depth[k++] = d;
    for(int u : t[v]){
      if(u != p){
	dfs(u,v,d+1,k,t);
	tour[k] = v;
	depth[k++] = d;
      }
    }
  }

  inline int lca(int u, int v){
    int a = min(id[u],id[v]), b = max(id[u],id[v]);
    return tour[dr.rmqi(a,b+1)];
  }
  inline int dep(int v){ return depth[id[v]]; }
};

//Segment Tree for Range Add and Sum
class SegmentTree{
  int n;
  vl all, part;
public:
  SegmentTree(int n_){
    n=1;
    while(n<n_)n*=2;
    all = part = vl(2*n-1,0);
  }
  
  void range_add(int a, int b, ll v, int k=0, int l=0, int r=0){
    if(l>=r)r = n;
    if(r<=a || b<=l)return;
    if(a<=l && r<=b){
      all[k] += v; return;
    }
    part[k] += v * (min(r,b)-max(l,a));
    range_add(a,b,v,2*k+1,l,(l+r)/2);
    range_add(a,b,v,2*k+2,(l+r)/2,r);
  }

  ll range_sum(int a,int b,int k=0,int l=0,int r=0){
    if(l>=r)r = n;
    if(r<=a || b<=l)return 0;
    if(a<=l && r<=b)return all[k]*(r-l) + part[k];
    ll vl = range_sum(a,b,2*k+1,l,(l+r)/2);
    ll vr = range_sum(a,b,2*k+2,(l+r)/2,r);
    return vl + vr + all[k]*(min(r,b)-max(l,a));
  }
};


ll cost(int x, vi &open, vi &pos_open, vi &pos_close,
	SegmentTree &st_open, SegmentTree &st_close){
  int pos = open[x];
  int op = upper_bound(all(pos_open),pos) - pos_open.begin();
  int cp = upper_bound(all(pos_close),pos) - pos_close.begin();

  return st_open.range_sum(0,op) - st_close.range_sum(0,cp);
}

int main(){
  int n,q;
  scanf("%d%d",&n,&q);
  vector<vi> tree(n);
  rep(i,n-1){
    int a,b;
    scanf("%d%d",&a,&b);
    tree[a].push_back(b);
  }

  LCA lca(tree,0);

  int azu=0;
  vi open(n,-1), close(n,-1);
  stack<int> s; s.push(0);
  while(s.size()){
    int v = s.top(); s.pop();
    if(open[v]<0){
      open[v] = azu++;
      s.push(v);
      for(int u : tree[v])s.push(u);
    }else{
      close[v] = azu++;
    }
  }

  vi pos_open = open, pos_close = close;
  sort(all(pos_open)); sort(all(pos_close));

  SegmentTree st_open(n), st_close(n);

  rep(i,q){
    int x,u,v;
    scanf("%d%d%d",&x,&u,&v);
    if(x==0){
      int w = lca.lca(u,v);
      ll res = cost(u, open, pos_open, pos_close, st_open, st_close)
	+ cost(v, open, pos_open, pos_close, st_open, st_close)
	- 2 * cost(w, open, pos_open, pos_close, st_open, st_close);
      printf("%lld\n",res);
    }else{
      int l = open[u], r = close[u];
      int olp = upper_bound(all(pos_open),l) - pos_open.begin();
      int orp = lower_bound(all(pos_open),r) - pos_open.begin();
      int clp = upper_bound(all(pos_close),l) - pos_close.begin();
      int crp = lower_bound(all(pos_close),r) - pos_close.begin();

      st_open.range_add(olp,orp,v);
      st_close.range_add(clp,crp,v);
    }
  }
}