【bzoj3052】[wc2013]糖果公園 帶修改樹上莫隊
阿新 • • 發佈:2017-10-27
algorithm cstring fine per sort 表示 clas %d ora
、時間(這次詢問之前的修改次數)為第一、二、三關鍵字排序,然後暴力移動三個指針。
題目描述
給出一棵n個點的樹,每個點有一個點權,點權範圍為1~m。支持兩種操作:(1)修改一個點的點權 (2)對於一條路徑,求$\sum\limits_{i=1}^m\sum\limits_{j=1}^{s_i}V_iW_j$,其中$s_i$表示這條鏈上權值為i的點數。
輸入
輸出
樣例輸入
4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2
樣例輸出
84
131
27
84
題解
帶修改樹上莫隊
帶修改樹上莫隊——普通莫隊的 帶修改進化版+上樹樹上進化版。
帶修改莫隊:每塊大小$n^{\frac 23}$,分別以左端點所在塊、右端點所在塊
樹上莫隊:分塊方法改成樹分塊(樹分塊方法參考 王室聯邦),自然序改成DFS序(然而帶修改莫隊用不到自然序,這裏只是提一嘴),暴力移動指針。這裏可以把點權加到邊權上(鏈上LCA只在統計答案是算上),以避免特判。
把它們結合起來本題就做完了。把樹分塊,然後按照帶修改莫隊處理即可。
幾點註意:
求LCA要用非樸素LCA(因為不僅僅在移動指針時需要求LCA,點權加到邊權上時統計答案也需要處理LCA)
修改要記錄是從什麽改到什麽,因為有反向修改(時間指針左移)操作。
答案會爆int,故需要long long。
時間復雜度$O(n^{\frac 53})$
#include <cstdio> #include <cstring> #include <algorithm> #define N 100010 using namespace std; const int b = 1200; typedef long long ll; int v[N] , w[N] , head[N] , to[N << 1] , next[N << 1] , ce , type[N]; int fa[N][20] , deep[N] , log[N] , tot , sta[N] , top , bl[N] , num; int pc[N] , wc[N] , vc[N] , vis[N] , cnt[N]; ll ans , ret[N]; struct data { int lp , rp , cp , id; bool operator<(const data &a)const { return bl[lp] == bl[a.lp] ? bl[rp] == bl[a.rp] ? cp < a.cp : bl[rp] < bl[a.rp] : bl[lp] < bl[a.lp]; } }a[N]; void add(int x , int y) { to[++ce] = y , next[ce] = head[x] , head[x] = ce; } void dfs(int x) { int i , now = top; for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1]; for(i = head[x] ; i ; i = next[i]) { if(to[i] != fa[x][0]) { fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]); if(top - now >= b) { num ++ ; while(top != now) bl[sta[top -- ]] = num; } } } sta[++top] = x; } int lca(int x , int y) { int i; if(deep[x] < deep[y]) swap(x , y); for(i = log[deep[x] - deep[y]] ; ~i ; i -- ) if(deep[x] - deep[y] >= (1 << i)) x = fa[x][i]; if(x == y) return x; for(i = log[deep[x]] ; ~i ; i -- ) if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i]) x = fa[x][i] , y = fa[y][i]; return fa[x][0]; } void rev(int x) { if(vis[x]) ans -= (ll)w[cnt[type[x]]] * v[type[x]] , cnt[type[x]] -- , vis[x] = 0; else cnt[type[x]] ++ , ans += (ll)w[cnt[type[x]]] * v[type[x]] , vis[x] = 1; } int main() { int n , m , q , i , j , x , y , opt , cc = 0 , ln = 1 , rn = 1 , now = 0; scanf("%d%d%d" , &n , &m , &q); for(i = 1 ; i <= m ; i ++ ) scanf("%d" , &v[i]); for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &w[i]); for(i = 2 ; i <= n ; i ++ ) scanf("%d%d" , &x , &y) , add(x , y) , add(y , x) , log[i] = log[i >> 1] + 1; dfs(1); while(top) bl[sta[top -- ]] = num; for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &type[i]); for(i = 1 ; i <= q ; i ++ ) { scanf("%d" , &opt); if(opt) { scanf("%d%d" , &a[i - cc].lp , &a[i - cc].rp) , a[i - cc].cp = cc , a[i - cc].id = i - cc; if(bl[a[i - cc].lp] > bl[a[i - cc].rp]) swap(a[i - cc].lp , a[i - cc].rp); } else cc ++ , scanf("%d%d" , &pc[cc] , &vc[cc]) , wc[cc] = type[pc[cc]] , type[pc[cc]] = vc[cc]; } for(i = cc ; i ; i -- ) type[pc[i]] = wc[i]; sort(a + 1 , a + q - cc + 1); for(i = 1 ; i <= q - cc ; i ++ ) { x = lca(ln , a[i].lp); for(j = ln ; j != x ; j = fa[j][0]) rev(j); for(j = a[i].lp ; j != x ; j = fa[j][0]) rev(j); x = lca(rn , a[i].rp); for(j = rn ; j != x ; j = fa[j][0]) rev(j); for(j = a[i].rp ; j != x ; j = fa[j][0]) rev(j); ln = a[i].lp , rn = a[i].rp , x = lca(ln , rn) , rev(x); while(now < a[i].cp) { now ++ ; if(vis[pc[now]]) cnt[vc[now]] ++ , ans += (ll)w[cnt[vc[now]]] * v[vc[now]] - (ll)w[cnt[wc[now]]] * v[wc[now]] , cnt[wc[now]] -- ; type[pc[now]] = vc[now]; } while(now > a[i].cp) { if(vis[pc[now]]) cnt[wc[now]] ++ , ans += (ll)w[cnt[wc[now]]] * v[wc[now]] - (ll)w[cnt[vc[now]]] * v[vc[now]] , cnt[vc[now]] -- ; type[pc[now]] = wc[now]; now -- ; } ret[a[i].id] = ans; rev(x); } for(i = 1 ; i <= q - cc ; i ++ ) printf("%lld\n" , ret[i]); return 0; }
【bzoj3052】[wc2013]糖果公園 帶修改樹上莫隊