MX galaxy Day2/3
Luogu P4211
差分之后扫描线。
深度可以转化为到根路径节点个数。
\(lca\) 深度可以变成一个节点到根路径加,另一个节点到根路径求和。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
const int mod = 201314;
typedef long long ll;
int n, m, ans[_];
int top[_], dep[_], fa[_], sz[_], son[_], dfn[_], idx;
int sum[_], tag[_];
std::vector <std::pair<int, int> > q[_];
std::vector <int> e[_];
void Dfs(int u) {
sz[u] = 1, dep[u] = dep[fa[u]] + 1;
for (int v : e[u]) {
Dfs(v), sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void Dfs(int u, int tp) {
top[u] = tp, dfn[u] = ++idx;
if (!son[u]) return; Dfs(son[u], tp);
for (int v : e[u]) if (v != son[u]) Dfs(v, v);
}
#define ls p << 1
#define rs p << 1 | 1
#define mid (s + t >> 1)
void pu(int p) { sum[p] = (sum[ls] + sum[rs]) % mod; }
void upd(int p, int k, int s, int t) { sum[p] = (sum[p] + k * (t - s + 1) % mod) % mod, tag[p] = (tag[p] + k) % mod; }
void pd(int p, int s, int t) { if (tag[p]) upd(ls, tag[p], s, mid), upd(rs, tag[p], mid + 1, t), tag[p] = 0; }
void mdy(int l, int r, int s, int t, int p) {
if (r < s or t < l) return;
if (l <= s and t <= r) return upd(p, 1, s, t); pd(p, s, t);
mdy(l, r, s, mid, ls), mdy(l, r, mid + 1, t, rs); pu(p);
}
int qry(int l, int r, int s, int t, int p) {
if (r < s or t < l) return 0;
if (l <= s and t <= r) return sum[p]; pd(p, s, t);
return (qry(l, r, s, mid, ls) + qry(l, r, mid + 1, t, rs)) % mod;
}
#undef ls
#undef rs
#undef mid
void Mdy(int x) { while (x) mdy(dfn[top[x]], dfn[x], 1, n, 1), x = fa[top[x]]; }
int Qry(int x) { int res = 0;
while (x) res = (res + qry(dfn[top[x]], dfn[x], 1, n, 1)) % mod, x = fa[top[x]];
return res;
}
int main() {
scanf("%d%d", & n, & m);
lep(i, 2, n) scanf("%d", fa + i), ++fa[i], e[fa[i]].push_back(i);
Dfs(1), Dfs(1, 1);
int l, r, x;
lep(i, 1, m) scanf("%d%d%d", & l, & r, & x), ++l, ++r, ++x,
q[l - 1].push_back({x, -i}), q[r].push_back({x, i});
lep(u, 1, n) { Mdy(u);
for (auto t : q[u]) { int x = t.first, i = t.second;
if (i > 0) ans[i] = (ans[i] + Qry(x)) % mod;
else ans[-i] = (ans[i] - Qry(x)) % mod;
}
}
lep(i, 1, m) printf("%d\n", (ans[i] % mod + mod) % mod);
return 0;
}
Luogu P6798
差分后考虑一段值域前缀 \([1, R]\) 。
假设到根路径上一个点子树内除去 \(a\) 后的最大值是 \(v\) 。
如果 \(v > R\) ,则贡献为 \(v\) 。
否则,贡献为 \(\frac{v^2-v+R^2+R}{2}\) 。
分界点可以二分,同时 \(v\) 的值一段是次大值,一段是最大值,同样二分分界点。
加上树上前缀和即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e5 + 7;
const int V = 20;
const int mod = 998244353;
const int Inv2 = (mod + 1) / 2;
typedef long long ll;
int n, q, opt, fa[_][V + 1], pos[_];
ll sum[_][2], sum_2[_][2], a[_], dep[_], ans, All, mx[_], se[_];
std::vector <int> e[_];
void Init(int u, int f) {
dep[u] = dep[fa[u][0] = f] + 1, mx[u] = u;
lep(i, 1, V) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int v : e[u]) if (v != f) {
Init(v, u);
if (a[mx[v]] > a[mx[u]]) se[u] = a[se[v]] > a[mx[u]] ? se[v] : mx[u], mx[u] = mx[v];
else se[u] = a[mx[v]] > a[se[u]] ? mx[v] : se[u];
}
}
void Init(int u) {
All = (All + a[mx[u]]) % mod;
sum[u][0] = (sum[fa[u][0]][0] + a[mx[u]]) % mod, sum[u][1] = (sum[fa[u][0]][1] + a[se[u]]) % mod;
sum_2[u][0] = (sum_2[fa[u][0]][0] + a[mx[u]] * a[mx[u]] % mod) % mod,
sum_2[u][1] = (sum_2[fa[u][0]][1] + a[se[u]] * a[se[u]] % mod) % mod;
int p = u;
rep(i, V, 0) if (a[mx[fa[p][i]]] == a[u] and a[se[fa[p][i]]] < a[u]) p = fa[p][i];
pos[u] = a[se[u]] < a[u] ? fa[p][0] : u;
for (int v : e[u]) if (v != fa[u][0]) Init(v);
}
ll S2(int u, int v, int o) { return (sum_2[v][o] - sum_2[u][o]) % mod; }
ll S(int u, int v, int o) { return (sum[v][o] - sum[u][o]) % mod; }
ll Get(ll R, int x) {
ll res = (All - sum[x][0]) * R % mod;
int l = x, r = pos[x];
rep(i, V, 0) if (dep[fa[l][i]] > dep[r] and a[se[fa[l][i]]] < R) l = fa[l][i];
if (r != x and a[se[l]] < R) res = (res + (S2(fa[l][0], x, 1) - S(fa[l][0], x, 1)) % mod * Inv2 % mod) % mod,
res = (res + (dep[x] - dep[l] + 1) * (R * (R + 1) % mod) % mod * Inv2 % mod) % mod;
if (r != x) { int q = a[se[l]] < R ? fa[l][0] : l;
res = (res + R * S(r, q, 1) % mod) % mod;
}
int c = r;
rep(i, V, 0) if (fa[c][i] and a[mx[fa[c][i]]] < R) c = fa[c][i];
if (c and a[mx[c]] < R) res = (res + (S2(fa[c][0], r, 0) - S(fa[c][0], r, 0)) % mod * Inv2 % mod) % mod,
res = (res + (dep[r] - dep[c] + 1) * (R * (R + 1) % mod) % mod * Inv2 % mod) % mod;
c = c and a[mx[c]] < R ? fa[c][0] : c;
res = (res + R * S(0, c, 0) % mod) % mod;
return res;
}
int main() {
scanf("%d%d%d", & n, & q, & opt);
lep(i, 1, n) scanf("%lld", a + i); int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
Init(1, 0), Init(1);
int l, r, x;
while (q--) {
scanf("%d%d%d", & l, & r, & x);
l = (l + opt * ans % n) % n + 1, r = (r + opt * ans % n) % n + 1, x = (x + opt * ans % n) % n + 1;
if (l > r) std::swap(l, r);
ans = (Get(r, x) - Get(l - 1, x)) % mod;
ans = (ans + mod) % mod;
printf("%lld\n", ans);
}
return 0;
}
CF1009F
考虑暴力 \(dp\) ,记 \(f[u, i]\) 为 \(u\) 子树内距离 \(u\) 为 \(i\) 的点的个数。
我们通过长链剖分和指针,让每个链头继承长儿子的信息,其他儿子暴力合并。
复杂度的话,每个点只会在链头被作为轻儿子合并一次,是 \(O(n)\) 的。
实现上,定义一个 \(O(n)\) 的数组,将首指针存下来,每次给链头分配相应高度的空间。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 1e6 + 7;
typedef long long ll;
int n, son[_], ans[_], hg[_], tl[_]; int* dp[_], *nw = tl;
std::vector <int> e[_];
void Init(int u, int f) {
hg[u] = 1;
for (int v : e[u]) if (v != f) {
Init(v, u), hg[u] = std::max(hg[u], hg[v] + 1);
if (hg[v] > hg[son[u]]) son[u] = v;
}
}
void Dfs(int u, int f) {
dp[u][0] = 1;
if (!son[u]) return;
dp[son[u]] = dp[u] + 1, Dfs(son[u], u);
ans[u] = ans[son[u]] + 1;
for (int v : e[u]) if (v != f and v != son[u]) {
dp[v] = nw, nw += hg[v], Dfs(v, u);
lep(i, 1, hg[v]) {
dp[u][i] += dp[v][i - 1];
if (dp[u][i] > dp[u][ans[u]] or dp[u][i] == dp[u][ans[u]] and i < ans[u]) ans[u] = i;
}
}
if (dp[u][ans[u]] == 1) ans[u] = 0;
}
int main() {
scanf("%d", & n); int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
Init(1, 0);
dp[1] = nw, nw += hg[1]; Dfs(1, 0);
lep(i, 1, n) printf("%d\n", ans[i]);
return 0;
}
Luogu P5903
对于每个链头,向上和向下各记录高度个点。
处理倍增数组,每次先跳到 \(x\) 的 \(2^{\log k}\) 级祖先的链头,然后可以利用记录直接读取。
空间上,我们只记录了 \(O(n)\) 个点。
时间上是 \(O(n\log n) \sim O(1)\) 的。
正确性的话,链头到 \(fa_k\) 距离一定小于链长,否则倍增一定会往上再跳一次。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e5 + 7;
const int V = 21;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
int lg[_], n, m, dep[_], hg[_], son[_], top[_], fa[_][V + 1], rt; u32 s; u64 ans, sum;
std::vector <int> e[_], g[_];
inline u32 get(u32 x) {
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return s = x;
}
void Init(int u) {
dep[u] = dep[fa[u][0]] + 1, hg[u] = 1;
lep(i, 1, V) fa[u][i] = fa[fa[u][i - 1]][i - 1];
for (int v : e[u]) {
Init(v);
hg[u] = std::max(hg[u], hg[v] + 1);
if (hg[v] > hg[son[u]]) son[u] = v;
}
}
void Init(int u, int tp) {
top[u] = tp, g[tp].push_back(u);
if (!son[u]) return; Init(son[u], tp);
for (int v : e[u]) if (v != son[u]) Init(v, v);
if (u == tp) {
int len = hg[u];
while (len-- and fa[u][0]) g[tp].push_back(u = fa[u][0]);
}
}
u32 qry(int x, int k) {
if (!k) return x;
int y = top[fa[x][lg[k]]];
if (dep[x] - k >= dep[y]) return g[y][dep[x] - k - dep[y]];
return g[y][dep[y] - dep[x] + k - 1 + hg[y]];
}
int main() {
scanf("%d%d%u", & n, & m, & s);
lep(i, 1, n) {
scanf("%d", fa[i]);
if (fa[i][0]) e[fa[i][0]].push_back(i);
else rt = i;
if (i > 1) lg[i] = lg[i >> 1] + 1;
}
Init(rt), Init(rt, rt);
lep(i, 1, m) {
int x = (get(s) ^ sum) % n + 1, k = (get(s) ^ sum) % dep[x];
sum = qry(x, k);
ans ^= sum * i;
}
printf("%llu\n", ans);
return 0;
}
qoj#9492. 树上简单求和
两棵树分别树剖,转变为区间问题。
相当于二维平面上 \(n\) 个散点,横坐标区间加,纵坐标区间求和,这个问题只能做到 \(O(\sqrt n)\) 复杂度。
分块,考虑散块和整块分开计算修改贡献。
散块,总共有 \(O(m\log n \sqrt n)\) 个修改,\(O(m\log n)\) 个查询。
使用值域分块做到 \(O(1)\) 修改 \(O(\sqrt n)\) 查询来平衡根号。
对于整块,按照时间顺序扫描,对另一维预处理 \(pos_i\) 表示每个坐标的前缀中有多少个位置被当前块所影响。
差分计算即可。
注意空间,要分别对每个整块处理,共用一份空间。
时间复杂度 \(O(n\sqrt n \log n)\) ,空间复杂度 \(O(n\log n)\) 。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
typedef long long ll;
typedef unsigned long long u64;
struct qry { int l, r, id; };
int n, m, bel[_], st[500], ed[500], B, pos[_], t[_]; u64 a[_], ans[_], val[_], sum[500], K[_];
int X[_], Y[_];
std::vector <int> o;
std::vector <qry> ql, qr, q;
struct Graph{
int ud[_], dfn[_], top[_], dep[_], son[_], sz[_], fa[_], idx;
std::vector <int> e[_];
void Input() { int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
}
void Dfs1(int u, int f) {
sz[u] = 1, dep[u] = dep[fa[u] = f] + 1;
for (int v : e[u]) if (v != f) {
Dfs1(v, u), sz[u] += sz[v];
if (sz[v] > sz[son[u]]) son[u] = v;
}
}
void Dfs2(int u, int tp) {
top[u] = tp, dfn[u] = ++idx, ud[idx] = u;
if (!son[u]) return; Dfs2(son[u], tp);
for (int v : e[u]) if (v != son[u] and v != fa[u]) Dfs2(v, v);
}
void Init() { Input(), Dfs1(1, 0), Dfs2(1, 1); }
void Add(int x, u64 k) { val[x] += k, sum[bel[x]] += k; }
void Calc(int l, int r, u64 k, int id) {
ql.push_back({l, r, id});
if (bel[l] == bel[r]) { lep(i, l, r) Add(t[i], k); return; }
lep(i, l, ed[bel[l]]) Add(t[i], k);
lep(i, st[bel[r]], r) Add(t[i], k);
}
void Calc(int l, int r, int id) {
u64 res = 0; qr.push_back( { l, r, id } );
if (bel[l] == bel[r]) { lep(i, l, r) res += val[i]; ans[id] += res; return; }
lep(i, l, ed[bel[l]]) res += val[i];
lep(i, bel[l] + 1, bel[r] - 1) res += sum[i];
lep(i, st[bel[r]], r) res += val[i];
ans[id] += res;
}
void Sca(int x, int y, u64 k, int id) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
Calc(dfn[top[x]], dfn[x], k, id);
x = fa[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
Calc(dfn[x], dfn[y], k, id);
}
void Solve(int x, int y, int id) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
Calc(dfn[top[x]], dfn[x], id);
x = fa[top[x]];
}
if (dep[x] > dep[y]) std::swap(x, y);
Calc(dfn[x], dfn[y], id);
}
}T1, T2;
int main() {
scanf("%d%d", & n, & m); B = std::sqrt(n);
lep(i, 1, n) {
scanf("%llu", a + i), bel[i] = (i - 1) / B + 1;
if (!st[bel[i]]) st[bel[i]] = i; ed[bel[i]] = i;
}
T1.Init(), T2.Init();
lep(i, 1, n) t[i] = T2.dfn[T1.ud[i]];
lep(i, 1, n) T1.Sca(i, i, a[i], 0);
int x, y; u64 k;
lep(i, 1, m) {
scanf("%d%d%llu", & x, & y, & k);
X[i] = x, Y[i] = y, K[i] = k;
T1.Sca(x, y, k, i), T2.Solve(x, y, i);
}
lep(i, 1, bel[n]) {
lep(j, 1, n) x = T1.dfn[T2.ud[j]], pos[j] = pos[j - 1] + (st[i] <= x and x <= ed[i]);
o.clear(), q.clear();
for (auto t : ql) if (t.l < st[i] and ed[i] < t.r) o.push_back(t.id);
for (auto t : qr) if (pos[t.r] - pos[t.l - 1]) q.push_back(t);
int L = 0, R = 0; u64 k = 0;
while (L < o.size() and R < q.size()) {
if (o[L] <= q[R].id) k += K[o[L]], ++L;
else ans[q[R].id] += (pos[q[R].r] - pos[q[R].l - 1]) * k, ++R;
}
while (R < q.size()) ans[q[R].id] += (pos[q[R].r] - pos[q[R].l - 1]) * k, ++R;
}
lep(i, 1, m) printf("%llu\n", ans[i]);
return 0;
}
qoj#7855. 不跳棋
点分树上每个节点开一个桶下标为子树内节点到自己距离。
开一个全局桶维护经过每个分治中心的最短路径。
每个分治中心的最短路径通过最小值和次小值拼出,即使在同一棵子树内也没关系,因为最小答案一定是在那一棵公共子树内。
删点时暴力移动最/次小值和答案指针即可,因为只有 \(O(n\log n)\) 量级,移动到末尾就没有了。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 5e5 + 7;
const int V = 20;
typedef long long ll;
int n, tp, ans, idx; ll cnt[_], sum;
int dfn[_], dep[_], st[_][V + 1], fa[_], pr[_], lg[_];
int sz[_], tmp[_], rt; bool vis[_];
int pos[_][2];
std::vector <int> e[_], tot[_];
void Init(int u, int f) {
dep[u] = dep[fa[u] = f] + 1, dfn[u] = ++idx, st[idx][0] = u;
for (int v : e[u]) if (v != f) Init(v, u);
}
int upd(int x, int y) { return dep[x] < dep[y] ? x : y; }
void Init() {
Init(1, 0);
lep(j, 1, V) lep(i, 1, n - (1 << j) + 1)
st[i][j] = upd(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
lep(i, 2, n) lg[i] = lg[i >> 1] + 1;
}
int lca(int x, int y) {
if (dfn[x] > dfn[y]) std::swap(x, y);
int k = lg[dfn[y] - dfn[x] + 1], p = upd(st[dfn[x]][k], st[dfn[y] - (1 << k) + 1][k]);
return x == p ? x : fa[p];
}
int len(int x, int y) { return dep[x] + dep[y] - 2 * dep[lca(x, y)]; }
void getrt(int u, int f, int total) {
sz[u] = 1, tmp[u] = 0;
for (int v : e[u]) if (!vis[v] and v != f)
getrt(v, u, total), sz[u] += sz[v], tmp[u] = std::max(tmp[u], sz[v]);
tmp[u] = std::max(tmp[u], total - sz[u]);
if (!rt or tmp[u] < tmp[rt]) rt = u;
}
int Build(int u, int total) {
rt = 0, getrt(u, 0, total), vis[rt] = true; int p = rt;
tot[p].resize(total + 3);
for (int v : e[rt]) if (!vis[v]) pr[Build(v, total - tmp[v])] = p;
return p;
}
void Jump(int x) { int u = x; while (u) ++tot[u][len(u, x)], u = pr[u]; }
void Push(int p) {
while (pos[p][0] < tot[p].size() - 1 and !tot[p][pos[p][0]]) ++pos[p][0];
while (pos[p][1] < tot[p].size() - 1 and ((tot[p][pos[p][0]] == 1 and pos[p][0] == pos[p][1]) or !tot[p][pos[p][1]])) ++pos[p][1];
}
ll calc(int p) {
if (pos[p][0] == pos[p][1]) return 1ll * tot[p][pos[p][0]] * (tot[p][pos[p][0]] - 1) / 2;
return 1ll * tot[p][pos[p][0]] * tot[p][pos[p][1]];
}
void Del(int x) {
int u = x;
while (u) {
cnt[pos[u][0] + pos[u][1]] -= calc(u), --tot[u][len(u, x)]; Push(u);
cnt[pos[u][0] + pos[u][1]] += calc(u);
while (!cnt[ans]) ++ans;
u = pr[u];
}
}
int main() {
scanf("%d%d", & n, & tp); int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
Init(), Build(1, n);
lep(i, 1, n) Jump(i);
lep(i, 1, n) Push(i), cnt[pos[i][0] + pos[i][1]] += calc(i);
ans = 1;
int x;
rep(i, n - 2, 1) {
scanf("%d", & x); x = (x ^ (tp * sum));
Del(x);
printf("%d %lld\n", ans, sum = cnt[ans]);
}
return 0;
}
qoj#9533. Classical Counting Problem
\([l, r]\) 的点全部加入,\(l\) 和 \(r\) 所在的连通块就是以 \(l\) 为最小值, \(r\) 的最大值的树,这样的树如果存在,一定是唯一的。
假如权值是 \(min \times max\) ,考虑怎么做。
这样就变成了一个路径问题,要求两个端点分别是路径的最小值和最大值,点分治即可。
现在加上了一个 \(siz\) ,我们考虑 拆贡献。
记三元组 \((l, r, k)\) 表示 \(k\) 可以属于 \(l\) 和 \(r\) 所确定的树内,每个三元组的贡献是 \(l \times r\) 。
依旧可以点分治,考虑跨过分治中心的答案。
(以下路径皆指代到分治中心的路径)
有三个候选部分 \(l\), \(r\), \(k\) 。
满足端点是路径最小值的加入 \(l\) ,端点是路径最大值的加入 \(r\) ,无论如何都要加入 \(k\) 。
每个部分存储一个二元组 \((mn, mx)\) 表示路径最小 / 大值。
一个合法的三元组满足以下条件:
\(lmn \le rmn\) , \(lmx \le rmx\) , \(lmn \le kmn\) , \(kmx \le rmx\) 。
将 \(lmx \le rmx\) , \(kmx \le rmx\) 用扫描线维护。
接下来只剩下 \(mn\) 之间的限制(下面在写法上省略)。
我们的问题是,目前所有的满足 \(l\le r\) \(\wedge\) \(l\le k\) 的 \((l, k)\) 点对贡献 \(l\) ,我们要计数。
使用线段树维护值域,点对数量是可以 PushUp() 得到的,最后贡献再乘上 \(rmx\) 即可。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
typedef long long ll;
typedef unsigned int u32;
typedef std::pair<int, int> PII;
int n;
struct SegTree {
struct node { int l, k; u32 t;
node(int _l = 0, int _k = 0, u32 _t = 0) { l = _l, k = _k, t = _t; }
friend node operator + (const node& x, const node& y) {
return node(x.l + y.l, x.k + y.k, x.t + y.t + 1u * x.l * y.k);
}
}tr[_ << 2];
int tot, ls[_ << 2], rs[_ << 2], rt;
void Cls() { tot = rt = 0; }
int frsh() { int p = ++tot; tr[p] = node(), ls[p] = rs[p] = 0; return p; }
#define mid ((s + t) >> 1)
void pu(int p) { tr[p] = tr[ls[p]] + tr[rs[p]]; }
void upd(int p, PII& v) { tr[p].l += v.first, tr[p].k += v.second; tr[p].t = 1ll * tr[p].l * tr[p].k; }
void mdy(int d, int s, int t, PII& v, int &p) {
if (!p) p = frsh();
if (s == t) return upd(p, v);
d <= mid ? mdy(d, s, mid, v, ls[p]) : mdy(d, mid + 1, t, v, rs[p]); pu(p);
}
void Mdy(int d, PII& v) { mdy(d, 1, n, v, rt); }
node qry(int l, int r, int s, int t, int p) {
if (!p or r < s or t < l) return node();
if (l <= s and t <= r) return tr[p];
return qry(l, r, s, mid, ls[p]) + qry(l, r, mid + 1, t, rs[p]);
}
u32 Qry(int l, int r) { return qry(l, r, 1, n, rt).t; }
#undef mid
}T;
int Test; u32 ans; PII l[_], r[_], k[_], nw; int cl, cr, ck;
int sz[_], tmp[_], rt; bool vis[_];
std::vector <int> e[_];
void getrt(int u, int f, int total) {
sz[u] = 1, tmp[u] = 0;
for (int v : e[u]) if (v != f and !vis[v])
getrt(v, u, total), sz[u] += sz[v], tmp[u] = std::max(tmp[u], sz[v]);
tmp[u] = std::max(tmp[u], total - sz[u]);
if (!rt or tmp[u] < tmp[rt]) rt = u;
}
void dfs(int u, int f, int mn, int mx) {
mn = std::min(mn, u), mx = std::max(mx, u);
if (mn == u) l[++cl] = { mx, mn };
if (mx == u) r[++cr] = { mx, mn };
k[++ck] = { mx, mn };
for (int v : e[u]) if (!vis[v] and v != f) dfs(v, u, mn, mx);
}
u32 calc(int u, int mn, int mx) {
u32 res = 0; cl = cr = ck = 0; dfs(u, 0, mn, mx);
int i = 1, j = 1;
std::sort(l + 1, l + 1 + cl), std::sort(r + 1, r + 1 + cr), std::sort(k + 1, k + 1 + ck);
T.Cls();
lep(p, 1, cr) {
while (i <= cl and l[i].first <= r[p].first) T.Mdy(l[i].second, nw = { l[i].second, 0 }), ++i;
while (j <= ck and k[j].first <= r[p].first) T.Mdy(k[j++].second, nw = { 0, 1 });
res += 1u * r[p].first * (T.Qry(1, n) - T.Qry(r[p].second + 1, n));
}
return res;
}
void Solve(int u, int total) {
rt = 0, getrt(u, 0, total), ans += calc(rt, n + 1, 0), vis[rt] = true;
for (int v : e[rt]) if (!vis[v]) ans -= calc(v, rt, rt);
for (int v : e[rt]) if (!vis[v]) Solve(v, total - tmp[v]);
}
int main() {
scanf("%d", & Test);
while (Test--) {
scanf("%d", & n); int u, v;
lep(i, 2, n) scanf("%d%d", & u, & v),
e[u].push_back(v), e[v].push_back(u);
Solve(1, n);
printf("%u\n", ans); ans = 0;
lep(i, 1, n) e[i].clear(), vis[i] = false;
}
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

拆贡献!
拆贡献!
拆贡献!
浙公网安备 33010602011771号