算法总复杂度: $O(n\sqrt{n} )$
-下面的代码中 `mot` 表示答案的分母 (mother), `sub` 表示分子, `sqn` 表示块的大小: $\sqrt{n}$ , `arr` 是输入的数组, `node` 是存储询问的结构体, `tab` 是询问序列(排序后的), `col` 同上所述。<strong >注意:下面代码中的移动区间的 4 个 for 循环的位置很关键,不能改变它们之间的位置关系,不然会 WA(因为有那个 `++l` 和 `--r` )。</strong>代码:
-
-```cpp
-#include <bits/stdc++.h>
-#define bi(a) ((a - 1) / sqn + 1)
-using namespace std;
-typedef long long LL;
-template <typename tp>
-void read(tp& dig) {
- char c = getchar();
- dig = 0;
- while (!isdigit(c)) c = getchar();
- while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar();
-}
-struct node {
- LL l, r, i;
-};
-LL n, m, sqn, arr[50005], l, r, ans, col[50005], sub[50005], mot[50005];
-vector<node> tab;
-bool cmp(node a, node b) {
- if (bi(a.l) == bi(b.l)) return a.r < b.r;
- return a.l < b.l;
-}
-LL gcd(LL a, LL b) { return !b ? a : gcd(b, a % b); }
-int main() {
- read(n), read(m), sqn = sqrt(n);
- for (LL i = 1; i <= n; i++) read(arr[i]);
- for (LL i = 1, a, b; i <= m; i++)
- read(a), read(b), tab.push_back((node){a, b, i});
- sort(tab.begin(), tab.end(), cmp), l = r = tab[0].l, col[arr[l]]++;
- for (LL i = 0, gcdnum; i < tab.size(); i++) {
- for (; l < tab[i].l; l++) col[arr[l]]--, ans -= col[arr[l]];
- for (--l; l >= tab[i].l; l--) ans += col[arr[l]], col[arr[l]]++;
- for (; r > tab[i].r; r--) col[arr[r]]--, ans -= col[arr[r]];
- for (++r; r <= tab[i].r; r++) ans += col[arr[r]], col[arr[r]]++;
- sub[tab[i].i] = ans, l = tab[i].l, r = tab[i].r;
- mot[tab[i].i] = ((r - l) * (r - l + 1)) >> 1;
- }
- for (LL i = 1, gcdn; i <= m; i++)
- gcdn = gcd(sub[i], mot[i]),
- printf("%lld/%lld\n", sub[i] / gcdn, mot[i] / gcdn);
- return 0;
-}
-```
+下面的代码中 `mot` 表示答案的分母 (mother), `sub` 表示分子, `sqn` 表示块的大小: $\sqrt{n}$ , `arr` 是输入的数组, `node` 是存储询问的结构体, `tab` 是询问序列(排序后的), `col` 同上所述。
+
+**注意:下面代码中的移动区间的 4 个 for 循环的位置很关键,不能改变它们之间的位置关系,不然会 WA(因为有那个 `++l` 和 `--r` )。**
+
+??? 参考代码
+
+ ```cpp
+ #include <bits/stdc++.h>
+ #define bi(a) ((a - 1) / sqn + 1)
+ using namespace std;
+ typedef long long LL;
+ template <typename tp>
+ void read(tp& dig) {
+ char c = getchar();
+ dig = 0;
+ while (!isdigit(c)) c = getchar();
+ while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar();
+ }
+ struct node {
+ LL l, r, i;
+ };
+ LL n, m, sqn, arr[50005], l, r, ans, col[50005], sub[50005], mot[50005];
+ vector<node> tab;
+ bool cmp(node a, node b) {
+ if (bi(a.l) == bi(b.l)) return a.r < b.r;
+ return a.l < b.l;
+ }
+ LL gcd(LL a, LL b) { return !b ? a : gcd(b, a % b); }
+ int main() {
+ read(n), read(m), sqn = sqrt(n);
+ for (LL i = 1; i <= n; i++) read(arr[i]);
+ for (LL i = 1, a, b; i <= m; i++)
+ read(a), read(b), tab.push_back((node){a, b, i});
+ sort(tab.begin(), tab.end(), cmp), l = r = tab[0].l, col[arr[l]]++;
+ for (LL i = 0, gcdnum; i < tab.size(); i++) {
+ for (; l < tab[i].l; l++) col[arr[l]]--, ans -= col[arr[l]];
+ for (--l; l >= tab[i].l; l--) ans += col[arr[l]], col[arr[l]]++;
+ for (; r > tab[i].r; r--) col[arr[r]]--, ans -= col[arr[r]];
+ for (++r; r <= tab[i].r; r++) ans += col[arr[r]], col[arr[r]]++;
+ sub[tab[i].i] = ans, l = tab[i].l, r = tab[i].r;
+ mot[tab[i].i] = ((r - l) * (r - l + 1)) >> 1;
+ }
+ for (LL i = 1, gcdn; i <= m; i++)
+ gcdn = gcd(sub[i], mot[i]),
+ printf("%lld/%lld\n", sub[i] / gcdn, mot[i] / gcdn);
+ return 0;
+ }
+ ```
## 普通莫队的优化
4 100
```
-手动模拟一下可以发现,r 指针的移动次数大概为 300 次,我们处理完第一个块之后, $l = 2, r = 100$ ,此时只需要移动两次 l 指针就可以得到第四个询问的答案,但是我们却将 r 指针移动到 1 来获取第三个询问的答案,再移动到 100 获取第四个询问的答案,这样多了九十几次的指针移动。我们怎么优化这个地方呢?这里我们就要用到奇偶化排序
+手动模拟一下可以发现,r 指针的移动次数大概为 300 次,我们处理完第一个块之后, $l = 2, r = 100$ ,此时只需要移动两次 l 指针就可以得到第四个询问的答案,但是我们却将 r 指针移动到 1 来获取第三个询问的答案,再移动到 100 获取第四个询问的答案,这样多了九十几次的指针移动。我们怎么优化这个地方呢?这里我们就要用到奇偶化排序。
-什么是奇偶化排序?奇偶化排序即对于属于奇数块的询问,r 按从小到大排序,对于属于偶数块的排序,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快 30% 左右
+什么是奇偶化排序?奇偶化排序即对于属于奇数块的询问,r 按从小到大排序,对于属于偶数块的排序,r 从大到小排序,这样我们的 r 指针在处理完这个奇数块的问题后,将在返回的途中处理偶数块的问题,再向 n 移动处理下一个奇数块的问题,优化了 r 指针的移动次数,一般情况下,这种优化能让程序快 30% 左右。
排序代码:
### 例题
-[数颜色 BZOJ - 2120](https://www.lydsy.com/JudgeOnline/problem.php?id=2120)
-
-题目大意:给你一个序列,M 个操作,有两种操作:
+!!! [数颜色 BZOJ - 2120](https://www.lydsy.com/JudgeOnline/problem.php?id=2120)
-1. 修改序列上某一位的数字
-2. 询问区间 $[l,r]$ 中数字的种类数(多个相同的数字只算一个)
+ 题目大意:给你一个序列,M 个操作,有两种操作:
+
+ 1. 修改序列上某一位的数字
+ 2. 询问区间 $[l,r]$ 中数字的种类数(多个相同的数字只算一个)
我们不难发现,如果不带操作 1(修改)的话,我们就能轻松用普通莫队解决。
因此这道题就这样用带修改莫队轻松解决啦!
-代码:
-
-```cpp
-#include <bits/stdc++.h>
-#define SZ (10005)
-using namespace std;
-template <typename _Tp>
-inline void IN(_Tp& dig) {
- char c;
- dig = 0;
- while (c = getchar(), !isdigit(c))
- ;
- while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar();
-}
-int n, m, sqn, c[SZ], ct[SZ], c1, c2, mem[SZ][3], ans, tot[1000005], nal[SZ];
-struct query {
- int l, r, i, c;
- bool operator<(const query another) const {
- if (l / sqn == another.l / sqn) {
- if (r / sqn == another.r / sqn) return i < another.i;
- return r < another.r;
+??? 参考代码
+
+ ```cpp
+ #include <bits/stdc++.h>
+ #define SZ (10005)
+ using namespace std;
+ template <typename _Tp>
+ inline void IN(_Tp& dig) {
+ char c;
+ dig = 0;
+ while (c = getchar(), !isdigit(c))
+ ;
+ while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar();
}
- return l < another.l;
- }
-} Q[SZ];
-void add(int a) {
- if (!tot[a]) ans++;
- tot[a]++;
-}
-void del(int a) {
- tot[a]--;
- if (!tot[a]) ans--;
-}
-char opt[10];
-int main() {
- IN(n), IN(m), sqn = pow(n, (double)2 / (double)3);
- for (int i = 1; i <= n; i++) IN(c[i]), ct[i] = c[i];
- for (int i = 1, a, b; i <= m; i++)
- if (scanf("%s", opt), IN(a), IN(b), opt[0] == 'Q')
- Q[c1].l = a, Q[c1].r = b, Q[c1].i = c1, Q[c1].c = c2, c1++;
- else
- mem[c2][0] = a, mem[c2][1] = ct[a], mem[c2][2] = ct[a] = b, c2++;
- sort(Q, Q + c1), add(c[1]);
- int l = 1, r = 1, lst = 0;
- for (int i = 0; i < c1; i++) {
- for (; lst < Q[i].c; lst++) {
- if (l <= mem[lst][0] && mem[lst][0] <= r)
- del(mem[lst][1]), add(mem[lst][2]);
- c[mem[lst][0]] = mem[lst][2];
+ int n, m, sqn, c[SZ], ct[SZ], c1, c2, mem[SZ][3], ans, tot[1000005], nal[SZ];
+ struct query {
+ int l, r, i, c;
+ bool operator<(const query another) const {
+ if (l / sqn == another.l / sqn) {
+ if (r / sqn == another.r / sqn) return i < another.i;
+ return r < another.r;
+ }
+ return l < another.l;
+ }
+ } Q[SZ];
+ void add(int a) {
+ if (!tot[a]) ans++;
+ tot[a]++;
}
- for (; lst > Q[i].c; lst--) {
- if (l <= mem[lst - 1][0] && mem[lst - 1][0] <= r)
- del(mem[lst - 1][2]), add(mem[lst - 1][1]);
- c[mem[lst - 1][0]] = mem[lst - 1][1];
+ void del(int a) {
+ tot[a]--;
+ if (!tot[a]) ans--;
}
- for (++r; r <= Q[i].r; r++) add(c[r]);
- for (--r; r > Q[i].r; r--) del(c[r]);
- for (--l; l >= Q[i].l; l--) add(c[l]);
- for (++l; l < Q[i].l; l++) del(c[l]);
- nal[Q[i].i] = ans;
- }
- for (int i = 0; i < c1; i++) printf("%d\n", nal[i]);
- return 0;
-}
-```
+ char opt[10];
+ int main() {
+ IN(n), IN(m), sqn = pow(n, (double)2 / (double)3);
+ for (int i = 1; i <= n; i++) IN(c[i]), ct[i] = c[i];
+ for (int i = 1, a, b; i <= m; i++)
+ if (scanf("%s", opt), IN(a), IN(b), opt[0] == 'Q')
+ Q[c1].l = a, Q[c1].r = b, Q[c1].i = c1, Q[c1].c = c2, c1++;
+ else
+ mem[c2][0] = a, mem[c2][1] = ct[a], mem[c2][2] = ct[a] = b, c2++;
+ sort(Q, Q + c1), add(c[1]);
+ int l = 1, r = 1, lst = 0;
+ for (int i = 0; i < c1; i++) {
+ for (; lst < Q[i].c; lst++) {
+ if (l <= mem[lst][0] && mem[lst][0] <= r)
+ del(mem[lst][1]), add(mem[lst][2]);
+ c[mem[lst][0]] = mem[lst][2];
+ }
+ for (; lst > Q[i].c; lst--) {
+ if (l <= mem[lst - 1][0] && mem[lst - 1][0] <= r)
+ del(mem[lst - 1][2]), add(mem[lst - 1][1]);
+ c[mem[lst - 1][0]] = mem[lst - 1][1];
+ }
+ for (++r; r <= Q[i].r; r++) add(c[r]);
+ for (--r; r > Q[i].r; r--) del(c[r]);
+ for (--l; l >= Q[i].l; l--) add(c[l]);
+ for (++l; l < Q[i].l; l++) del(c[l]);
+ nal[Q[i].i] = ans;
+ }
+ for (int i = 0; i < c1; i++) printf("%d\n", nal[i]);
+ return 0;
+ }
+ ```
## 树上莫队
然后因为所包含的区间内可能没有 LCA,对于没有的情况要将多余的贡献删除,然后就完事了
-code:
-
-```cpp
-#include <algorithm>
-#include <cmath>
-#include <cstdio>
-#include <iostream>
-
-#define DEBUG printf("line:%d func:%s\n", __LINE__, __FUNCTION__);
-
-using namespace std;
-
-const int maxn = 200010;
-
-int f[maxn], g[maxn], id[maxn], head[maxn], cnt, last[maxn], dep[maxn],
- fa[maxn][22], v[maxn], w[maxn];
-int block, index, n, m, q;
-int pos[maxn], col[maxn], app[maxn];
-bool vis[maxn];
-long long ans[maxn], cur;
-
-struct edge {
- int to, nxt;
-} e[maxn];
-int cnt1 = 0, cnt2 = 0; // 时间戳
-
-struct query {
- int l, r, t, id;
- bool operator<(const query &b) const {
- return (pos[l] < pos[b.l]) || (pos[l] == pos[b.l] && pos[r] < pos[b.r]) ||
- (pos[l] == pos[b.l] && pos[r] == pos[b.r] && t < b.t);
- }
-} a[maxn], b[maxn];
-
-inline void addedge(int x, int y) {
- e[++cnt] = (edge){y, head[x]};
- head[x] = cnt;
-}
-
-void dfs(int x) {
- id[f[x] = ++index] = x;
- for (int i = head[x]; i; i = e[i].nxt) {
- if (e[i].to != fa[x][0]) {
- fa[e[i].to][0] = x;
- dep[e[i].to] = dep[x] + 1;
- dfs(e[i].to);
- }
- }
- id[g[x] = ++index] = x; // 括号序
-}
-
-inline void swap(int &x, int &y) {
- int t;
- t = x;
- x = y;
- y = t;
-}
-
-inline int lca(int x, int y) {
- if (dep[x] < dep[y]) swap(x, y);
- if (dep[x] != dep[y]) {
- int dis = dep[x] - dep[y];
- for (int i = 20; i >= 0; i--)
- if (dis >= (1 << i)) dis -= 1 << i, x = fa[x][i];
- } // 爬到同一高度
- if (x == y) return x;
- for (int i = 20; i >= 0; i--) {
- if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
- }
- return fa[x][0];
-}
-
-inline void add(int x) {
- if (vis[x])
- cur -= (long long)v[col[x]] * w[app[col[x]]--];
- else
- cur += (long long)v[col[x]] * w[++app[col[x]]];
- vis[x] ^= 1;
-}
-
-inline void modify(int x, int t) {
- if (vis[x]) {
- add(x);
- col[x] = t;
- add(x);
- } else
- col[x] = t;
-} // 在时间维上移动
-
-int main() {
- scanf("%d%d%d", &n, &m, &q);
- for (int i = 1; i <= m; i++) scanf("%d", &v[i]);
- for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
- for (int i = 1; i < n; i++) {
- int x, y;
- scanf("%d%d", &x, &y);
- addedge(x, y);
- addedge(y, x);
- }
- for (int i = 1; i <= n; i++) {
- scanf("%d", &last[i]);
- col[i] = last[i];
- }
- dfs(1);
- for (int j = 1; j <= 20; j++)
- for (int i = 1; i <= n; i++)
- fa[i][j] = fa[fa[i][j - 1]][j - 1]; // 预处理祖先
- int block = pow(index, 2.0 / 3);
- for (int i = 1; i <= index; i++) {
- pos[i] = (i - 1) / block;
- }
- while (q--) {
- int opt, x, y;
- scanf("%d%d%d", &opt, &x, &y);
- if (opt == 0) {
- b[++cnt2].l = x;
- b[cnt2].r = last[x];
- last[x] = b[cnt2].t = y;
- } else {
- if (f[x] > f[y]) swap(x, y);
- a[++cnt1] = (query){lca(x, y) == x ? f[x] : g[x], f[y], cnt2, cnt1};
- }
- }
- sort(a + 1, a + cnt1 + 1);
- int L, R, T; // 指针坐标
- L = R = 0;
- T = 1;
- for (int i = 1; i <= cnt1; i++) {
- while (T <= a[i].t) {
- modify(b[T].l, b[T].t);
- T++;
+??? 参考代码
+
+ ```cpp
+ #include <algorithm>
+ #include <cmath>
+ #include <cstdio>
+ #include <iostream>
+
+ #define DEBUG printf("line:%d func:%s\n", __LINE__, __FUNCTION__);
+
+ using namespace std;
+
+ const int maxn = 200010;
+
+ int f[maxn], g[maxn], id[maxn], head[maxn], cnt, last[maxn], dep[maxn],
+ fa[maxn][22], v[maxn], w[maxn];
+ int block, index, n, m, q;
+ int pos[maxn], col[maxn], app[maxn];
+ bool vis[maxn];
+ long long ans[maxn], cur;
+
+ struct edge {
+ int to, nxt;
+ } e[maxn];
+ int cnt1 = 0, cnt2 = 0; // 时间戳
+
+ struct query {
+ int l, r, t, id;
+ bool operator<(const query &b) const {
+ return (pos[l] < pos[b.l]) || (pos[l] == pos[b.l] && pos[r] < pos[b.r]) ||
+ (pos[l] == pos[b.l] && pos[r] == pos[b.r] && t < b.t);
+ }
+ } a[maxn], b[maxn];
+
+ inline void addedge(int x, int y) {
+ e[++cnt] = (edge){y, head[x]};
+ head[x] = cnt;
}
- while (T > a[i].t) {
- modify(b[T].l, b[T].r);
- T--;
+
+ void dfs(int x) {
+ id[f[x] = ++index] = x;
+ for (int i = head[x]; i; i = e[i].nxt) {
+ if (e[i].to != fa[x][0]) {
+ fa[e[i].to][0] = x;
+ dep[e[i].to] = dep[x] + 1;
+ dfs(e[i].to);
+ }
+ }
+ id[g[x] = ++index] = x; // 括号序
}
- while (L > a[i].l) {
- L--;
- add(id[L]);
+
+ inline void swap(int &x, int &y) {
+ int t;
+ t = x;
+ x = y;
+ y = t;
}
- while (L < a[i].l) {
- add(id[L]);
- L++;
+
+ inline int lca(int x, int y) {
+ if (dep[x] < dep[y]) swap(x, y);
+ if (dep[x] != dep[y]) {
+ int dis = dep[x] - dep[y];
+ for (int i = 20; i >= 0; i--)
+ if (dis >= (1 << i)) dis -= 1 << i, x = fa[x][i];
+ } // 爬到同一高度
+ if (x == y) return x;
+ for (int i = 20; i >= 0; i--) {
+ if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
+ }
+ return fa[x][0];
}
- while (R > a[i].r) {
- add(id[R]);
- R--;
+
+ inline void add(int x) {
+ if (vis[x])
+ cur -= (long long)v[col[x]] * w[app[col[x]]--];
+ else
+ cur += (long long)v[col[x]] * w[++app[col[x]]];
+ vis[x] ^= 1;
}
- while (R < a[i].r) {
- R++;
- add(id[R]);
+
+ inline void modify(int x, int t) {
+ if (vis[x]) {
+ add(x);
+ col[x] = t;
+ add(x);
+ } else
+ col[x] = t;
+ } // 在时间维上移动
+
+ int main() {
+ scanf("%d%d%d", &n, &m, &q);
+ for (int i = 1; i <= m; i++) scanf("%d", &v[i]);
+ for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
+ for (int i = 1; i < n; i++) {
+ int x, y;
+ scanf("%d%d", &x, &y);
+ addedge(x, y);
+ addedge(y, x);
+ }
+ for (int i = 1; i <= n; i++) {
+ scanf("%d", &last[i]);
+ col[i] = last[i];
+ }
+ dfs(1);
+ for (int j = 1; j <= 20; j++)
+ for (int i = 1; i <= n; i++)
+ fa[i][j] = fa[fa[i][j - 1]][j - 1]; // 预处理祖先
+ int block = pow(index, 2.0 / 3);
+ for (int i = 1; i <= index; i++) {
+ pos[i] = (i - 1) / block;
+ }
+ while (q--) {
+ int opt, x, y;
+ scanf("%d%d%d", &opt, &x, &y);
+ if (opt == 0) {
+ b[++cnt2].l = x;
+ b[cnt2].r = last[x];
+ last[x] = b[cnt2].t = y;
+ } else {
+ if (f[x] > f[y]) swap(x, y);
+ a[++cnt1] = (query){lca(x, y) == x ? f[x] : g[x], f[y], cnt2, cnt1};
+ }
+ }
+ sort(a + 1, a + cnt1 + 1);
+ int L, R, T; // 指针坐标
+ L = R = 0;
+ T = 1;
+ for (int i = 1; i <= cnt1; i++) {
+ while (T <= a[i].t) {
+ modify(b[T].l, b[T].t);
+ T++;
+ }
+ while (T > a[i].t) {
+ modify(b[T].l, b[T].r);
+ T--;
+ }
+ while (L > a[i].l) {
+ L--;
+ add(id[L]);
+ }
+ while (L < a[i].l) {
+ add(id[L]);
+ L++;
+ }
+ while (R > a[i].r) {
+ add(id[R]);
+ R--;
+ }
+ while (R < a[i].r) {
+ R++;
+ add(id[R]);
+ }
+ int x = id[L], y = id[R];
+ int llca = lca(x, y);
+ if (x != llca && y != llca) {
+ add(llca);
+ ans[a[i].id] = cur;
+ add(llca);
+ } else
+ ans[a[i].id] = cur;
+ }
+ for (int i = 1; i <= cnt1; i++) {
+ printf("%lld\n", ans[i]);
+ }
+ return 0;
}
- int x = id[L], y = id[R];
- int llca = lca(x, y);
- if (x != llca && y != llca) {
- add(llca);
- ans[a[i].id] = cur;
- add(llca);
- } else
- ans[a[i].id] = cur;
- }
- for (int i = 1; i <= cnt1; i++) {
- printf("%lld\n", ans[i]);
- }
- return 0;
-}
-```
+ ```
## 真·树上莫队
}
```
-对于求 LCA,我们可以用树剖,然后我们就可以把分块的步骤放到树剖的第一次 dfs 里面,时间戳也可以直接用第二次 dfs 的 dfs 序~~(如果你用 tarjan 就当我没说)~~
+对于求 LCA,我们可以用树剖,然后我们就可以把分块的步骤放到树剖的第一次 dfs 里面,时间戳也可以直接用第二次 dfs 的 dfs 序。
```cpp
int bl[100002], bls = 0; //属于的块,块的数量
由于多了时间维,块的大小取到 0.6 的样子就差不多了
-```cpp
-#include <bits/stdc++.h>
-//#pragma GCC optimize(2)
-using namespace std;
-inline int gi() {
- register int x, c, op = 1;
- while (c = getchar(), c < '0' || c > '9')
- if (c == '-') op = -op;
- x = c ^ 48;
- while (c = getchar(), c >= '0' && c <= '9')
- x = (x << 3) + (x << 1) + (c ^ 48);
- return x * op;
-}
-int head[100002], nxt[200004], ver[200004], tot = 0;
-void add(int x, int y) {
- ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
- ver[++tot] = x, nxt[tot] = head[y], head[y] = tot;
-}
-int bl[100002], bls = 0;
-unsigned step;
-int fa[100002], dp[100002], hs[100002] = {0}, sz[100002] = {0}, top[100002],
- id[100002];
-stack<int> sta;
-void dfs1(int x) {
- sz[x] = 1;
- unsigned ss = sta.size();
- for (int i = head[x]; i; i = nxt[i])
- if (ver[i] != fa[x]) {
- fa[ver[i]] = x, dp[ver[i]] = dp[x] + 1;
- dfs1(ver[i]);
- sz[x] += sz[ver[i]];
- if (sz[ver[i]] > sz[hs[x]]) hs[x] = ver[i];
- if (sta.size() - ss >= step) {
+??? 参考代码
+
+ ```cpp
+ #include <bits/stdc++.h>
+ //#pragma GCC optimize(2)
+ using namespace std;
+ inline int gi() {
+ register int x, c, op = 1;
+ while (c = getchar(), c < '0' || c > '9')
+ if (c == '-') op = -op;
+ x = c ^ 48;
+ while (c = getchar(), c >= '0' && c <= '9')
+ x = (x << 3) + (x << 1) + (c ^ 48);
+ return x * op;
+ }
+ int head[100002], nxt[200004], ver[200004], tot = 0;
+ void add(int x, int y) {
+ ver[++tot] = y, nxt[tot] = head[x], head[x] = tot;
+ ver[++tot] = x, nxt[tot] = head[y], head[y] = tot;
+ }
+ int bl[100002], bls = 0;
+ unsigned step;
+ int fa[100002], dp[100002], hs[100002] = {0}, sz[100002] = {0}, top[100002],
+ id[100002];
+ stack<int> sta;
+ void dfs1(int x) {
+ sz[x] = 1;
+ unsigned ss = sta.size();
+ for (int i = head[x]; i; i = nxt[i])
+ if (ver[i] != fa[x]) {
+ fa[ver[i]] = x, dp[ver[i]] = dp[x] + 1;
+ dfs1(ver[i]);
+ sz[x] += sz[ver[i]];
+ if (sz[ver[i]] > sz[hs[x]]) hs[x] = ver[i];
+ if (sta.size() - ss >= step) {
+ bls++;
+ while (sta.size() != ss) bl[sta.top()] = bls, sta.pop();
+ }
+ }
+ sta.push(x);
+ }
+ int cnt = 0;
+ void dfs2(int x, int hf) {
+ top[x] = hf, id[x] = ++cnt;
+ if (!hs[x]) return;
+ dfs2(hs[x], hf);
+ for (int i = head[x]; i; i = nxt[i])
+ if (ver[i] != fa[x] && ver[i] != hs[x]) dfs2(ver[i], ver[i]);
+ }
+ int lca(int x, int y) {
+ while (top[x] != top[y]) {
+ if (dp[top[x]] < dp[top[y]]) swap(x, y);
+ x = fa[top[x]];
+ }
+ return dp[x] < dp[y] ? x : y;
+ }
+ struct qu {
+ int x, y, t, id;
+ bool operator<(const qu a) const {
+ return bl[x] == bl[a.x] ? (bl[y] == bl[a.y] ? t < a.t : bl[y] < bl[a.y])
+ : bl[x] < bl[a.x];
+ }
+ } q[100001];
+ int qs = 0;
+ struct ch {
+ int x, y, b;
+ } upd[100001];
+ int ups = 0;
+ long long ans[100001];
+ int b[100001] = {0};
+ int a[100001];
+ long long w[100001];
+ long long v[100001];
+ long long now = 0;
+ bool vis[100001] = {0};
+ void back(int t) {
+ if (vis[upd[t].x]) {
+ now -= w[b[upd[t].y]--] * v[upd[t].y];
+ now += w[++b[upd[t].b]] * v[upd[t].b];
+ }
+ a[upd[t].x] = upd[t].b;
+ }
+ void change(int t) {
+ if (vis[upd[t].x]) {
+ now -= w[b[upd[t].b]--] * v[upd[t].b];
+ now += w[++b[upd[t].y]] * v[upd[t].y];
+ }
+ a[upd[t].x] = upd[t].y;
+ }
+ void update(int x) {
+ if (vis[x])
+ now -= w[b[a[x]]--] * v[a[x]];
+ else
+ now += w[++b[a[x]]] * v[a[x]];
+ vis[x] ^= 1;
+ }
+ void move(int x, int y) {
+ if (dp[x] < dp[y]) swap(x, y);
+ while (dp[x] > dp[y]) update(x), x = fa[x];
+ while (x != y) update(x), update(y), x = fa[x], y = fa[y];
+ }
+ int main() {
+ int n = gi(), m = gi(), k = gi();
+ step = (int)pow(n, 0.6);
+ for (int i = 1; i <= m; i++) v[i] = gi();
+ for (int i = 1; i <= n; i++) w[i] = gi();
+ for (int i = 1; i < n; i++) add(gi(), gi());
+ for (int i = 1; i <= n; i++) a[i] = gi();
+ for (int i = 1; i <= k; i++)
+ if (gi())
+ q[++qs].x = gi(), q[qs].y = gi(), q[qs].t = ups, q[qs].id = qs;
+ else
+ upd[++ups].x = gi(), upd[ups].y = gi();
+ for (int i = 1; i <= ups; i++) upd[i].b = a[upd[i].x], a[upd[i].x] = upd[i].y;
+ for (int i = ups; i; i--) back(i);
+ fa[1] = 1;
+ dfs1(1), dfs2(1, 1);
+ if (!sta.empty()) {
bls++;
- while (sta.size() != ss) bl[sta.top()] = bls, sta.pop();
+ while (!sta.empty()) bl[sta.top()] = bls, sta.pop();
+ }
+ for (int i = 1; i <= n; i++)
+ if (id[q[i].x] > id[q[i].y]) swap(q[i].x, q[i].y);
+ sort(q + 1, q + qs + 1);
+ int x = 1, y = 1, t = 0;
+ for (int i = 1; i <= qs; i++) {
+ if (x != q[i].x) move(x, q[i].x), x = q[i].x;
+ if (y != q[i].y) move(y, q[i].y), y = q[i].y;
+ int f = lca(x, y);
+ update(f);
+ while (t < q[i].t) change(++t);
+ while (t > q[i].t) back(t--);
+ ans[q[i].id] = now;
+ update(f);
}
+ for (int i = 1; i <= qs; i++) printf("%lld\n", ans[i]);
+ return 0;
}
- sta.push(x);
-}
-int cnt = 0;
-void dfs2(int x, int hf) {
- top[x] = hf, id[x] = ++cnt;
- if (!hs[x]) return;
- dfs2(hs[x], hf);
- for (int i = head[x]; i; i = nxt[i])
- if (ver[i] != fa[x] && ver[i] != hs[x]) dfs2(ver[i], ver[i]);
-}
-int lca(int x, int y) {
- while (top[x] != top[y]) {
- if (dp[top[x]] < dp[top[y]]) swap(x, y);
- x = fa[top[x]];
- }
- return dp[x] < dp[y] ? x : y;
-}
-struct qu {
- int x, y, t, id;
- bool operator<(const qu a) const {
- return bl[x] == bl[a.x] ? (bl[y] == bl[a.y] ? t < a.t : bl[y] < bl[a.y])
- : bl[x] < bl[a.x];
- }
-} q[100001];
-int qs = 0;
-struct ch {
- int x, y, b;
-} upd[100001];
-int ups = 0;
-long long ans[100001];
-int b[100001] = {0};
-int a[100001];
-long long w[100001];
-long long v[100001];
-long long now = 0;
-bool vis[100001] = {0};
-void back(int t) {
- if (vis[upd[t].x]) {
- now -= w[b[upd[t].y]--] * v[upd[t].y];
- now += w[++b[upd[t].b]] * v[upd[t].b];
- }
- a[upd[t].x] = upd[t].b;
-}
-void change(int t) {
- if (vis[upd[t].x]) {
- now -= w[b[upd[t].b]--] * v[upd[t].b];
- now += w[++b[upd[t].y]] * v[upd[t].y];
- }
- a[upd[t].x] = upd[t].y;
-}
-void update(int x) {
- if (vis[x])
- now -= w[b[a[x]]--] * v[a[x]];
- else
- now += w[++b[a[x]]] * v[a[x]];
- vis[x] ^= 1;
-}
-void move(int x, int y) {
- if (dp[x] < dp[y]) swap(x, y);
- while (dp[x] > dp[y]) update(x), x = fa[x];
- while (x != y) update(x), update(y), x = fa[x], y = fa[y];
-}
-int main() {
- int n = gi(), m = gi(), k = gi();
- step = (int)pow(n, 0.6);
- for (int i = 1; i <= m; i++) v[i] = gi();
- for (int i = 1; i <= n; i++) w[i] = gi();
- for (int i = 1; i < n; i++) add(gi(), gi());
- for (int i = 1; i <= n; i++) a[i] = gi();
- for (int i = 1; i <= k; i++)
- if (gi())
- q[++qs].x = gi(), q[qs].y = gi(), q[qs].t = ups, q[qs].id = qs;
- else
- upd[++ups].x = gi(), upd[ups].y = gi();
- for (int i = 1; i <= ups; i++) upd[i].b = a[upd[i].x], a[upd[i].x] = upd[i].y;
- for (int i = ups; i; i--) back(i);
- fa[1] = 1;
- dfs1(1), dfs2(1, 1);
- if (!sta.empty()) {
- bls++;
- while (!sta.empty()) bl[sta.top()] = bls, sta.pop();
- }
- for (int i = 1; i <= n; i++)
- if (id[q[i].x] > id[q[i].y]) swap(q[i].x, q[i].y);
- sort(q + 1, q + qs + 1);
- int x = 1, y = 1, t = 0;
- for (int i = 1; i <= qs; i++) {
- if (x != q[i].x) move(x, q[i].x), x = q[i].x;
- if (y != q[i].y) move(y, q[i].y), y = q[i].y;
- int f = lca(x, y);
- update(f);
- while (t < q[i].t) change(++t);
- while (t > q[i].t) back(t--);
- ans[q[i].id] = now;
- update(f);
- }
- for (int i = 1; i <= qs; i++) printf("%lld\n", ans[i]);
- return 0;
-}
-```
+ ```