Loading

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;
}


posted @ 2025-07-17 07:55  qkhm  阅读(47)  评论(0)    收藏  举报