1. 程式人生 > >洛谷 3676 - 小清新資料結構題

洛谷 3676 - 小清新資料結構題

說實話這題寫樹剖 $LCT$ 什麼的真的思想又不難又好實現的樣子,但是我還是選擇自虐選擇了動態點分治

那就兩種做法都稍微提一下:

樹鏈剖分 / $LCT$

很容易可以發現一個換根操作只會對當前根在原樹(根為 $1$ )上的祖先一條鏈造成影響,也就是將它們的子樹變成除當前鏈方向其它與之相連的點集,那麼用樹剖跳,用線段樹維護一下原樹上從上面來的和從下面來的,再將所有涉及的節點合併,並且刪去算重複的和不該算的即可(雖然我沒實現但是這個思路應該是對的

動態點分治

首先有兩篇部落格:zzq租酥雨

因為我們要算 $\sum\limits_{i = 1}^n s_i^2$ ,可以先想一下 $\sum\limits_{i = 1}^n s_i$ 怎麼算,因為每個點的貢獻只會被祖先計算到,那麼易知 $\sum\limits_{i = 1}^n s_i = \sum\limits_{i = 1}^n value_i * (depth_i + 1)$ ,這個直接用動態點分治維護三個變數 $sumo_i, sumt_i, sumfa_i$ (sumo -> $p$ 子節點權值之和,  $sumt$ -> 子節點權值與距離的乘積到 $p$ 之和,  $sumfa$ -> 子節點權值與距離的乘積到 $fa$ (點分樹上)之和)即可得到

接下來有個很重要的結論(反正我是肯定想不到

  - 不論根如何換, $\sum\limits_{i = 1}^n s_i (sum - s_i)$ 一定是一個定值(說實話一開始一直想著如何化簡 $\sum\limits_{i = 1}^n s_i$ ,所以是真的沒有想到可以通過構造定值的方法來解出 $\sum\limits_{i = 1}^n s_i^2$ )

先來意會一下這個結論:就是每一條邊連線的兩個點在他們的子樹中各自選兩個點讓它們權值相乘,求總權值

那麼就比較容易知道證明了:每條邊的邊權為所有對應路徑經過這條邊的兩個點的權值和,求總邊權,即求的是 $\sum\limits_{i = 1}^n \sum\limits_{j = 1}^n value_i * value_j * dist (i, j)$ ,所以有 $\sum\limits_{i = 1}^n s_i (sum - s_i) = \sum\limits_{i = 1}^n \sum\limits_{j = 1}^n value_i * value_j * dist (i, j)$

因為該式是個定值,所以求出 $\sum\limits_{i = 1}^n s_i$ 後直接解出 $Ans$ 就完成了查詢操作

對於修改操作,若修改完後與原權值的差值為 $\Delta value$ ,那麼 $\Delta total = \Delta value \sum\limits_{j = 1}^n value_j * dist (p, j)$ ( $p$ 為修改點)

程式碼

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5
#include <cmath> 6 7 using namespace std; 8 9 typedef long long LL; 10 11 const int MAXN = 2e05 + 10; 12 const int MAXM = 2e05 + 10; 13 14 const int INF = 0x7fffffff; 15 16 struct LinkedForwardStar { 17 int to; 18 19 int next; 20 } ; 21 22 LinkedForwardStar Link[MAXM << 1]; 23 int Head[MAXN]= {0}; 24 int size = 0; 25 26 void Insert (int u, int v) { 27 Link[++ size].to = v; 28 Link[size].next = Head[u]; 29 30 Head[u] = size; 31 } 32 33 int N, Q; 34 int value[MAXN]; 35 36 LL Deep[MAXN]= {0}; 37 int Dfn[MAXN]= {0}; 38 int val[MAXN << 1]= {0}, belong[MAXN << 1]= {0}; 39 int dfsord = 0; 40 LL sum = 0; 41 LL subtree[MAXN]= {0}; 42 LL s1 = 0; 43 void DFS (int root, int fa) { 44 Dfn[root] = ++ dfsord; 45 val[dfsord] = Deep[root], belong[dfsord] = root; 46 sum += value[root]; 47 subtree[root] = value[root]; 48 for (int i = Head[root]; i; i = Link[i].next) { 49 int v = Link[i].to; 50 if (v == fa) 51 continue; 52 Deep[v] = Deep[root] + 1; 53 DFS (v, root); 54 val[++ dfsord] = Deep[root], belong[dfsord] = root; 55 subtree[root] += subtree[v]; 56 } 57 } 58 pair<int, int> ST[MAXN << 1][25]; 59 void RMQ () { 60 for (int i = 1; i <= dfsord; i ++) 61 ST[i][0] = make_pair (val[i], belong[i]); 62 for (int j = 1; j <= 20; j ++) 63 for (int i = 1; i + (1 << j) - 1 <= dfsord; i ++) 64 ST[i][j] = ST[i][j - 1].first < ST[i + (1 << (j - 1))][j - 1].first ? ST[i][j - 1] : ST[i + (1 << (j - 1))][j - 1]; 65 } 66 int LCA (int x, int y) { 67 int L = Dfn[x], R = Dfn[y]; 68 if (L > R) 69 swap (L, R); 70 int k = log2 (R - L + 1); 71 return ST[L][k].first < ST[R - (1 << k) + 1][k].first ? ST[L][k].second : ST[R - (1 << k) + 1][k].second; 72 } 73 LL dist (int x, int y) { 74 int lca = LCA (x, y); 75 return Deep[x] + Deep[y] - (Deep[lca] << 1); 76 } 77 78 int father[MAXN]= {0}; 79 bool Vis[MAXN]= {false}; 80 int Size[MAXN]= {0}; 81 int minv = INF, grvy; 82 int total; 83 void Grvy_Acqu (int root, int fa) { 84 Size[root] = 1; 85 int maxpart = 0; 86 for (int i = Head[root]; i; i = Link[i].next) { 87 int v = Link[i].to; 88 if (v == fa || Vis[v]) 89 continue; 90 Grvy_Acqu (v, root); 91 Size[root] += Size[v]; 92 maxpart = max (maxpart, Size[v]); 93 } 94 maxpart = max (maxpart, total - Size[root]); 95 if (maxpart < minv) 96 minv = maxpart, grvy = root; 97 } 98 LL sumo[MAXN]= {0}, sumt[MAXN]= {0}, sumfa[MAXN]= {0}; 99 // sumo -> p子節點權值之和, sumt -> 子節點權值與距離的乘積到p之和, sumfa -> 子節點權值與距離的乘積到fa(點分樹上)之和 100 void sums_Acqu (int root, int fa) { 101 sumo[grvy] += value[root], sumt[grvy] += value[root] * dist (root, grvy); 102 if (father[grvy]) 103 sumfa[grvy] += value[root] * dist (root, father[grvy]); 104 for (int i = Head[root]; i; i = Link[i].next) { 105 int v = Link[i].to; 106 if (v == fa || Vis[v]) 107 continue; 108 sums_Acqu (v, root); 109 } 110 } 111 void point_DAC (int p, int pre) { 112 minv = INF, grvy = p, total = Size[p]; 113 Grvy_Acqu (p, 0); 114 Vis[grvy] = true, father[grvy] = pre; 115 sums_Acqu (grvy, 0); 116 int fgrvy = grvy; 117 for (int i = Head[fgrvy]; i; i = Link[i].next) { 118 int v = Link[i].to; 119 if (Vis[v]) 120 continue; 121 point_DAC (v, fgrvy); 122 } 123 } 124 125 LL Query (int op) { 126 LL tsum = 0; 127 for (int p = op; p; p = father[p]) { 128 tsum += sumt[p]; 129 if (p != op) 130 tsum += sumo[p] * dist (p, op); 131 if (father[p]) 132 tsum -= sumo[p] * dist (father[p], op) + sumfa[p]; 133 } 134 return tsum; 135 } 136 void Modify (int op, int delta) { 137 for (int p = op; p; p = father[p]) { 138 sumo[p] -= value[op] - delta; 139 sumt[p] -= value[op] * dist (p, op) - delta * dist (p, op); 140 if (father[p]) 141 sumfa[p] -= value[op] * dist (father[p], op) - delta * dist (father[p], op); 142 } 143 sum -= value[op] - delta; 144 LL s = Query (op); 145 s1 += (delta - value[op]) * s; 146 value[op] = delta; 147 } 148 149 int getnum () { 150 int num = 0; 151 char ch = getchar (); 152 int isneg = 0; 153 154 while (! isdigit (ch)) { 155 if (ch == '-') 156 isneg = 1; 157 ch = getchar (); 158 } 159 while (isdigit (ch)) 160 num = (num << 3) + (num << 1) + ch - '0', ch = getchar (); 161 162 return isneg ? - num : num; 163 } 164 165 int main () { 166 N = getnum (), Q = getnum (); 167 for (int i = 1; i < N; i ++) { 168 int u = getnum (), v = getnum (); 169 Insert (u, v), Insert (v, u); 170 } 171 for (int i = 1; i <= N; i ++) 172 value[i] = getnum (); 173 DFS (1, 0), RMQ (); 174 for (int i = 1; i <= N; i ++) 175 s1 += subtree[i] * (sum - subtree[i]); 176 Size[1] = N, point_DAC (1, 0); 177 /*cout << "Next----------------------" << endl; 178 for (int i = 1; i <= N; i ++) 179 cout << sumo[i] << ' ' << sumt[i] << ' ' << sumfa[i] << endl; 180 cout << "End-----------------------" << endl;*/ 181 for (int Case = 1; Case <= Q; Case ++) { 182 int opt = getnum (); 183 if (opt == 1) { 184 int p = getnum (), delta = getnum (); 185 Modify (p, delta); 186 } 187 else if (opt == 2) { 188 int p = getnum (); 189 LL ans = (Query (p) + sum) * sum - s1; 190 printf ("%lld\n", ans); 191 } 192 } 193 194 return 0; 195 } 196 197 /* 198 4 5 199 1 2 200 2 3 201 2 4 202 4 3 2 1 203 2 2 204 1 1 3 205 2 3 206 1 2 4 207 2 4 208 */ 209 210 /* 211 4 1 212 1 2 213 2 3 214 2 4 215 4 3 2 1 216 2 1 217 */