1. 程式人生 > >Luogu3676 小清新資料結構題 動態點分治

Luogu3676 小清新資料結構題 動態點分治

傳送門


換根型別的統計問題動態點分治都是很好做的。

設所有點的點權和為$sum$

首先,我們先不考慮求$\sum\limits_i s_i^2$,先考慮如何在換根的情況下求$\sum\limits_i s_i$。

考慮一個點$i$會被統計多少次,顯然是$dep_i+1$,那麼$\sum\limits_i s_i = \sum\limits_i (dep_i+1) \times val_i = \sum\limits_i dep_i \times val_i + sum$。

$\sum\limits_i dep_i \times val_i$是不是似曾相識……其實就是幻想鄉戰略遊戲

接著我們考慮這個煩人的平方項。那麼我們還需要推一個結論:換根不會影響$W=\sum\limits_i s_i \times (sum - s_i)$的值。

證明如下:

我們考慮$K = \sum\limits_i \sum\limits_j val_i \times val_j \times dis(i,j)$,意思就是取兩個點,將中間的所有邊設為兩邊的點的權值之積然後相加。顯然這一個值是不會因為根的變化而改變的。

接著我們考慮一條邊$(x,y)$對$K$的貢獻,顯然是這條邊分割開來的兩個子樹的權值和的乘積,而無論根是哪一個,這兩個子樹中必定有且僅有一個是樹的一個子樹,假設$x$對應的樹是子樹,它就代表著$s_x \times (sum - s_x)$,把所有邊的貢獻加起來就是$W=\sum\limits_i s_i \times (sum - s_i)=\sum\limits_i \sum\limits_j val_i \times val_j \times dis(i,j)$,所以$W$是不會因為根的變化而變化的。

而$W=\sum\limits_i s_i \times (sum - s_i) = sum \times \sum\limits_i s_i - \sum\limits_i s_i^2$,那麼$\sum\limits_i s_i^2 = sum \times \sum\limits_i s_i - W$

接著我們考慮修改點權對$W$的影響。由$\Delta W = \Delta v \times \sum\limits_j val_j \times dep_j$,實質跟上面計算$\sum\limits_i s_i$的方法一樣。

  1 #include<bits/stdc++.h>
  2
#define int long long 3 //This code is written by Itst 4 using namespace std; 5 6 inline int read(){ 7 int a = 0; 8 bool f = 0; 9 char c = getchar(); 10 while(c != EOF && !isdigit(c)){ 11 if(c == '-') 12 f = 1; 13 c = getchar(); 14 } 15 while(c != EOF && isdigit(c)){ 16 a = (a << 3) + (a << 1) + (c ^ '0'); 17 c = getchar(); 18 } 19 return f ? -a : a; 20 } 21 22 const int MAXN = 200010; 23 struct Edge{ 24 int end , upEd; 25 }Ed[MAXN << 1]; 26 int val[MAXN] , head[MAXN] , fa[MAXN][20] , dis[MAXN][20] , dep[MAXN]; 27 int ST[21][MAXN << 1] , fir[MAXN] , logg2[MAXN << 1] , size[MAXN] , cur[MAXN] , up[MAXN] , sum[MAXN]; 28 int cntEd , N , M , minSize , nowSize , minInd , cntST , W , allV; 29 bool vis[MAXN]; 30 31 inline void addEd(int a , int b){ 32 Ed[++cntEd].end = b; 33 Ed[cntEd].upEd = head[a]; 34 head[a] = cntEd; 35 } 36 37 void init_dfs(int x , int p){ 38 dep[x] = dep[p] + 1; 39 fir[x] = ++cntST; 40 sum[x] = val[x]; 41 ST[0][cntST] = x; 42 for(int i = head[x] ; i ; i = Ed[i].upEd) 43 if(Ed[i].end != p){ 44 init_dfs(Ed[i].end , x); 45 ST[0][++cntST] = x; 46 sum[x] += sum[Ed[i].end]; 47 } 48 W += sum[x] * (allV - sum[x]); 49 } 50 51 inline int cmp(int a , int b){ 52 return dep[a] < dep[b] ? a : b; 53 } 54 55 void init_st(){ 56 for(int i = 2 ; i <= cntST ; ++i) 57 logg2[i] = logg2[i >> 1] + 1; 58 for(int i = 1 ; 1 << i <= cntST ; ++i) 59 for(int j = 1 ; j + (1 << i) - 1 <= cntST ; ++j) 60 ST[i][j] = cmp(ST[i - 1][j] , ST[i - 1][j + (1 << (i - 1))]); 61 } 62 63 inline int LCA(int x , int y){ 64 x = fir[x]; 65 y = fir[y]; 66 if(x > y) 67 swap(x , y); 68 int t = logg2[y - x + 1]; 69 return cmp(ST[t][x] , ST[t][y - (1 << t) + 1]); 70 } 71 72 inline int calcLen(int x , int y){ 73 return dep[x] + dep[y] - (dep[LCA(x , y)] << 1); 74 } 75 76 void getSize(int x){ 77 vis[x] = 1; 78 ++nowSize; 79 for(int i = head[x] ; i ; i = Ed[i].upEd) 80 if(!vis[Ed[i].end]) 81 getSize(Ed[i].end); 82 vis[x] = 0; 83 } 84 85 void getRoot(int x){ 86 vis[x] = size[x] = 1; 87 int maxN = 0; 88 for(int i = head[x] ; i ; i = Ed[i].upEd) 89 if(!vis[Ed[i].end]){ 90 getRoot(Ed[i].end); 91 maxN = max(maxN , size[Ed[i].end]); 92 size[x] += size[Ed[i].end]; 93 } 94 maxN = max(maxN , nowSize - size[x]); 95 if(maxN < minSize){ 96 minSize = maxN; 97 minInd = x; 98 } 99 vis[x] = 0; 100 } 101 102 void init_dfz(int x , int p){ 103 nowSize = 0; 104 minSize = 0x7fffffff; 105 getSize(x); 106 getRoot(x); 107 x = minInd; 108 fa[x][0] = x; 109 fa[x][1] = p; 110 vis[x] = 1; 111 sum[x] = val[x]; 112 for(int i = 1 ; fa[x][i] ; ++i){ 113 fa[x][i + 1] = fa[fa[x][i]][1]; 114 dis[x][i] = calcLen(fa[x][i] , x); 115 up[fa[x][i - 1]] += dis[x][i] * val[x]; 116 sum[fa[x][i]] += val[x]; 117 cur[fa[x][i]] += dis[x][i] * val[x]; 118 } 119 for(int i = head[x] ; i ; i = Ed[i].upEd) 120 if(!vis[Ed[i].end]) 121 init_dfz(Ed[i].end , x); 122 } 123 124 void init(){ 125 init_dfs(1 , 0); 126 init_st(); 127 init_dfz(1 , 0); 128 } 129 130 inline int query(int x){ 131 int all = cur[x]; 132 for(int i = 1 ; fa[x][i] ; ++i){ 133 all += cur[fa[x][i]] - up[fa[x][i - 1]]; 134 all += (sum[fa[x][i]] - sum[fa[x][i - 1]]) * dis[x][i]; 135 } 136 return all; 137 } 138 139 inline void modify(int x , int v){ 140 W += (v - val[x]) * query(x); 141 allV += v - val[x]; 142 sum[x] += v - val[x]; 143 for(int i = 1 ; fa[x][i] ; ++i){ 144 up[fa[x][i - 1]] += (v - val[x]) * dis[x][i]; 145 cur[fa[x][i]] += (v - val[x]) * dis[x][i]; 146 sum[fa[x][i]] += v - val[x]; 147 } 148 val[x] = v; 149 } 150 151 signed main(){ 152 #ifndef ONLINE_JUDGE 153 freopen("3676.in" , "r" , stdin); 154 freopen("3676.out" , "w" , stdout); 155 #endif 156 N = read(); 157 M = read(); 158 for(int i = 1 ; i < N ; ++i){ 159 int a = read() , b = read(); 160 addEd(a , b); 161 addEd(b , a); 162 } 163 for(int i = 1 ; i <= N ; ++i){ 164 val[i] = read(); 165 allV += val[i]; 166 } 167 init(); 168 for(int i = 1 ; i <= M ; ++i) 169 if(read() == 1){ 170 int x = read() , y = read(); 171 modify(x , y); 172 } 173 else 174 cout << allV * (query(read()) + allV) - W << '\n'; 175 return 0; 176 }