#3551. 逆序
给出一颗树,点带二维点权 \(w=(p,q)\)。定义 \(f(S)\) 为满足以下条件的有序对 \((x,y,z)\) 的数量:
- \(x,y,z\) 互不相同。
- 记 \(a=\text{lca}(x,z),b=\text{lca}(y,z)\),有 \(w_a\) 严格偏序 \(w_b\)。
记 \(S_0=\set{1,2,\cdots,n}\),给出 \(q\) 个询问:
- 给出 \(x\),询问 \(f(S_0\setminus \set x)\)。
- 给出 \(x,y\),询问 \(f(S_0\setminus \set{x,y})\)。
先考虑如何求出 \(f(S_0)\)。
由于 \(a,b\) 均为 \(z\) 的祖先,进一步钦定 \(b\) 是 \(a\) 的祖先,即 \(a\prec b\) 表示 \(p_a<p_b \land q_a<q_b \land \text{b 为 a 祖先}\)。

由于偏序限制在 \(a,b\) 上,我们不妨钦定 \(a\) 对 \((x,z)\) 计数方案数 \(g(a)\),钦定 \(b\) 对 \(y\) 计数方案数 \(f(b)\)。
记 \(s_x\) 为 \(x\) 子树大小。
由 \(x,z\) 互异且 \(\text{lca}(x,z)=a\),有
记 \(b\) 在 \(a\) 方向上的儿子为 \(u\),则有:
由于 \(b\) 儿子不唯一,但 \(u\) 父亲唯一,为方便维护,不妨将 \(f\) 放在 \(u\) 上维护,下文均记为 \(f(u)\)。
那么任意偏序对 \(a\prec b\) 对答案贡献 \(f(u)\cdot g(a)\)。
不妨考虑三维偏序,第三维是祖先-后代限制,放在 dfs 序上即可转化为区间问题,可以 BIT 维护。
那么 cdq 即可,\(\mathcal O(n\log^2 n)\)。
考虑删去一个数的情况。记删去的数为 \(c\)。
不妨对所有 \(a\prec b\),记 \(G_u=\displaystyle\sum_a g(a)\),\(F_a=\displaystyle\sum_b f(u)\),均可在 cdq 过程内维护。
不难验证 \(f(S_0)=\displaystyle\sum F(a)g(a)=\sum f(u)G(u)\)。
考虑删去 \(c\) 对 \(f,g\) 的影响,记其在 \(x\) 处的影响为 \(\Delta_x\):
- 对于 \(c\) 本身,由于 \(s'_c=s_c-1\),有 \(g'(c)=g(c)-2s'_c\),则 \(\Delta_c=2s'_cF(c)\)。
- 对于 \(c\) 的每一个祖先 \(a\),有 \(g'(a)=g(a)-2s'_a+2s'_c\),则 \(\Delta_a=2(s'_a-s'_c)F(a)\)。
- 对于 \(u\) 的父亲 \(\text{fa}_u\) 是 \(c\) 的祖先,但 \(u\) 不是 \(c\) 的祖先,有\(f'(u)=f(u)-1\),则 \(\Delta_u=G(u)\)。
上述值可以在一次 dfs 内全部预处理。
考虑删去两个数的情况。记删去的数为 \(c,d\)。
不妨先将答案减去 \(\Delta_c+\Delta_d\),然后加回同时包含 \(c,d\) 的方案 \(\Delta'\)。
取 \(t=\text{lca}(c,d)\)。 对于同时包含 \(c,d\) 的方案,有两种情况:
- \(c,d\) 作为方案中的 \(x,z\) 出现。这种情况比较简单,有 \(\Delta'=F(t)\)。
- \(c,d\) 作为方案中的 \(x,y\) 出现:

记 \(t\) 的 \(c\) 方向上的儿子为 \(u_2\)。此时对于每个 \(r\) 为 \(c\) 的祖先但不是 \(u_2\) 的祖先,且 \(r\prec t\),记其 \(c\) 方向上的儿子为 \(u_1\),则其贡献 \(f(u_1)\) 种不同的 \(z\)。
不妨差分,记 \(h(u)\) 为 \(u\) 到根的路径上满足 \(u\prec t\) 的 \(u\) 的 \(f(u)\) 之和,则 \(\Delta'=h(c)-h(u_2)\)。不难在 cdq 过程中维护。
由于 \(x,y,z\) 的对称性,上面两种情况的 \(\Delta'\) 均需 \(\times 2\)。
至此即可解决这个问题,仅需两次 cdq,常数较小可以拿最优解(x
#include <algorithm>
#include <iostream>
#include <vector>
const int N = 1.5e5 + 7, M = 18;
#define rep(i,a,b) for(int i(a);i<=(b);++i)
typedef long long i64;
struct node { int p, q, id; } w[N];
int n, m;
std::basic_string<int> g[N];
int st[N], ed[N], fa[N], dep[N], tim;
i64 rf[N], rg[N], xf[N], xg[N], siz[N];
int rmq[M][N], jmp[N][M];
void dfs1(int u) {
if(fa[u]) g[u].erase(std::find(g[u].begin(), g[u].end(), fa[u]));
st[u] = ++tim, siz[u] = 1, rmq[0][st[u]] = fa[u];
jmp[u][0] = fa[u]; for(int j = 1; j < M; ++j) jmp[u][j] = jmp[jmp[u][j-1]][j-1];
for(int& v: g[u]) {
fa[v] = u, dep[v] = dep[u] + 1;
dfs1(v);
siz[u] += siz[v];
}
ed[u] = tim;
rg[u] = siz[u] * (siz[u] - 1);
for(int& v: g[u]) {
rf[v] = siz[u] - siz[v];
rg[u] -= siz[v] * (siz[v] - 1);
}
}
inline int _min(int x, int y) { return st[x] < st[y] ? x : y; }
inline void init() {
for(int j = 1; j < M; ++j)
for(int i = 1<<j; i <= n; ++i)
rmq[j][i] = _min(rmq[j-1][i], rmq[j-1][i-(1<<(j-1))]);
}
inline int lca(int x, int y) {
if((x = st[x]) > (y = st[y])) std::swap(x, y);
int k = std::__lg(y - x);
return _min(rmq[k][y], rmq[k][x+(1<<k)]);
}
inline int skip(int x, int d) {
for(int i = M-1; ~i; --i)
if(d >> i & 1) x = jmp[x][i];
return x;
}
struct BIT {
i64 f[N];
inline void _add(int x, i64 y) {
for(; x < N; x += x & -x)
f[x] += y;
}
inline void modify(int x, int y, i64 v) {
_add(x, v), _add(y+1, -v);
}
inline i64 _get(int x) {
i64 y = 0;
for(; x; x -= x & -x)
y += f[x];
return y;
}
inline i64 query(int x, int y) {
return _get(y) - (x ? _get(x - 1) : 0);
}
inline void _clear(int x) {
for(; x < N; x += x & -x)
f[x] = 0;
}
inline void clear(int x, int y) {
_clear(x), _clear(y+1);
}
} T;
auto Ct = [](auto&&f, auto&&...args) { f(f, args...); };
struct offq { int x, s, id; };
std::basic_string<offq> ofq[N];
i64 res[N];
inline i64 cdq() {
std::sort(w + 1, w + n + 1, [](auto&& x, auto&& y) {
return x.p < y.p;
});
i64 ans = 0;
static node tmp[N];
Ct([&](auto&&ptr, int l, int r) -> void {
int mid = (l + r) >> 1;
if(l < mid) ptr(ptr, l, mid);
if(mid + 1 < r) ptr(ptr, mid+1, r);
int j = l, k = mid+1, p = l;
while(j <= mid || k <= r) {
if(k > r || (j <= mid && w[j].q < w[k].q)) {
int& u = w[j].id;
for(int& v: g[u])
T.modify(st[v], ed[v], rf[v]);
tmp[p] = w[j], tmp[p++].id = j++;
} else {
int& u = w[k].id;
xf[u] += T._get(st[u]);
for(auto& [x, s, id]: ofq[u])
res[id] += T._get(st[x]) - T._get(st[s]);
tmp[p] = w[k], tmp[p++].id = k++;
}
}
for(int i = l; i <= mid; ++i)
for(int& v: g[w[i].id])
T.clear(st[v], ed[v]);
for(int i = r; i >= l; --i) {
int& u = w[tmp[i].id].id;
if(tmp[i].id > mid)
T._add(st[u], rg[u]);
else
for(int& v: g[u])
xg[v] += T.query(st[v], ed[v]);
tmp[i].id = u;
}
for(int i = mid+1; i <= r; ++i)
T._clear(st[w[i].id]);
for(int i = l; i <= r; ++i)
w[i] = tmp[i];
}, 1, n);
return ans;
}
i64 dt[N], df[N];
void dfs2(int u) {
if(fa[u]) dt[u] += xf[fa[u]] * 2 * (siz[fa[u]] - siz[u]);
df[u] += xf[u];
i64 sumdf = 0;
for(int& v: g[u]) sumdf += xg[v];
for(int& v: g[u]) {
dt[v] = dt[u] + (sumdf - xg[v]);
df[v] = df[u];
dfs2(v);
}
dt[u] += xf[u] * 2 * (siz[u] - 1) + sumdf;
}
std::pair<int, int> qry[N];
inline void solve() {
std::cin >> n >> m;
tim = 0;
rep(i, 1, n) {
xf[i] = xg[i] = 0;
g[i].clear(), ofq[i].clear();
}
rep(i, 1, n) {
std::cin >> w[i].p >> w[i].q, w[i].id = i;
w[i].q = n - w[i].q + 1;
}
for(int t = n, x, y; --t; ) {
std::cin >> x >> y;
g[x] += y, g[y] += x;
}
dfs1(1);
init();
for(int i = 1; i <= m; ++i) {
auto& [x, y] = qry[i];
int z; std::cin >> z >> x;
if(z == 2) {
res[i] = 0;
std::cin >> y;
int f = lca(x, y);
if(x != f) ofq[f] += {x, skip(x, dep[x] - dep[f] - 1), i};
if(y != f) ofq[f] += {y, skip(y, dep[y] - dep[f] - 1), i};
for(int v: {x, y})
if(v != f && (w[v].p < w[f].p) == (w[v].q < w[f].q))
res[i] += siz[v] - 1;
} else y = 0;
}
cdq();
rep(i, 1, n) w[i].p = n - w[i].p + 1, w[i].q = n - w[i].q + 1;
cdq();
i64 ans = 0;
for(int i = 1; i <= n; ++i )
ans += xg[i] * rf[i];
std::cout << ans << "\n";
dt[1] = df[1] = 0;
dfs2(1);
for(int i = 1; i <= m; ++i) {
auto& [x, y] = qry[i];
if(y) {
res[i] += xf[lca(x, y)];
std::cout << ans - dt[x] - dt[y] + 2 * res[i] << "\n";
} else std::cout << ans - dt[x] << "\n";
}
}
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int t; std::cin >> t; while(t--) solve();
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/22688220

浙公网安备 33010602011771号