题解:P17859 「GLR-R5」白露 / ros

· · 题解

做死我了。

考虑每次往序列末尾添加一个数 x,那么 x 是合法的当且仅当所有前面出现的 >x 的数都与 x 相邻。

设 L_x 表示 <x 的与 x 相邻的数构成的集合,R_x 表示 >x 的与 x 相邻的数构成的集合。

不妨先考虑记录最大值 v:令 f_v 表示最大值为 v 的合法序列个数。考虑添加一个数 x 后,如何得到最大值为 v 的合法序列。若添加的是 v,则贡献显然是 \sum\limits_{i=1}^vf_i;否则若添加的是 x\in L_v,则原序列最大值为 v,但是这里只保证 x\in L_v 不足以保证 x 合法。

这启发我们再去记录 g_{v,x} 表示最大值为 v、x 可以添加到序列末尾的序列个数。由于 x\in L_v,所以状态个数显然是 \mathcal{O}(n) 级别的。

这样很容易就能得到 f 的转移:

f'_v=\sum_{i=1}^vf_i+\sum_{x\in L_v}g_{v,x}

再来考虑 g 的转移。若原序列最大值为 v,则添加 v 或者 x 显然都不影响 x 的合法性。若添加满足 x<t<u 的数 t,则 x,t 都与 u 相邻,加入 t 后 x 必然不合法。若添加满足 t<x 的数 t,那么我们保证 t 可以被添加到末尾即可。

但是这里又出现了新的需要记录合法性的量。那咋办?

考虑先计算出 \sum\limits_{t<x\land t\in L_v}g_{v,t},但是这样会多算 x 不能添加到末尾的序列。

做一个重要的观察:若 x\in L_v 且 x 不能添加到末尾,则 <x 的可以被添加到末尾的数至多只有一个。这是因为,考虑 x 不合法说明存在 x<y<v 使得 x,y 不相邻,那么 <x 的合法的数必须同时与 y,v 相邻,显然至多只有一个。

这启发我们再去记录 h_{v,x} 表示最大值为 v、x 不能添加到序列末尾,但存在 t<x 能添加到序列末尾的序列个数。

这样我们直接减去 h_{v,x} 就能扣掉多算的部分了。原序列最大值为 v 的贡献就是 2g_{v,x}+\sum\limits_{t<x\land t\in L_v}g_{v,t}-h_{v,x}。

再来考虑原序列最大值为 u<v 的情况。此时我们保证 x 在原序列中合法即可。显然 u\leq x 的序列必然合法,而若 x<u<v 则要有 u\in R_x,贡献 g_{u,x}。

这样我们就得到了 g 的完整转移

g'_{v,x}=2g_{v,x}+\sum_{t<x\land t\in L_v}g_{v,t}-h_{v,x}+\sum_{u=1}^xf_u+\sum_{u\in R_x\land u<v}g_{u,x}

再来考虑 h 的转移。还是先考虑原序列最大值为 v 的情况。添加 v,t 都是合法的,若添加一个满足 x<p<v 的数 p 显然会使 t 变得不合法。而若原本 x 合法,则为了让它变得不合法必然会令 t<x 也变得不合法。因此贡献恰好就是 2h_{v,x}。

再来考虑原序列最大值为 u<v 的情况。显然要有 u>x,否则 x 不可能变得不合法,同时 t 要在原序列中合法。由此得到 h 的完整转移:

h'_{v,x}=2h_{v,x}+\sum_{t\in L_v\land t<x}\sum_{u\in R_t\land x<u<v}g_{u,t}

至此我们终于得到了所有 DP 转移。

暴力做上述转移的时间复杂度为 \mathcal{O}(n^2m),需要优化。

考虑 $h$ 的转移如何优化。观察到 $v,t$ 相邻且 $L_v,R_t$ 分别是 $\mathcal{O}(deg_v),\mathcal{O}(deg_t)$ 量级的。考虑固定 $v,t$,把转移重写成:枚举 $u\in R_t\land u<v$,再枚举 $x\in L_v\land t<x<u$,令 $h'_{v,x}\gets h'_{v,x}+g_{u,t}$。我们只要把转移做到 $\mathcal{O}(\min(deg_v,deg_t))$ 的复杂度总体就是 $\mathcal{O}(n)$ 的了。 若枚举 $u$,则相当于在 $L_v$ 的一个区间上的 $h$ 做区间加;若枚举 $x$,则相当于在 $R_t$ 的一个区间上求 $g$ 的区间和。取较小的的一侧做即可。 这样我们就把时间复杂度做到 $\mathcal{O}(nm+n\log{n})$。 大抵不是预期做法,所以非常卡常。需要把大部分 `vector` 改成 CSR 才能卡过去。 :::success[主要代码] ```cpp int tc, n, m, posL[MAXN], posR[MAXN]; int degL[MAXN], stL[MAXN], edL[MAXN]; int degR[MAXN], stR[MAXN], edR[MAXN]; pii edges[MAXN]; vector<int> pos[MAXN]; pii L[MAXN], R[MAXN]; mint df[MAXN], sum[MAXN]; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin >> tc; while (tc--) { cin >> n >> m; for (int i = 1; i <= n; ++i) { pos[i].clear(); degL[i] = degR[i] = 0; } for (int i = 1; i < n; ++i) { int u, v; cin >> u >> v; if (u > v) swap(u, v); edges[i] = {u, v}; ++degL[v]; ++degR[u]; } stL[1] = edL[1] = stR[1] = edR[1] = 0; for (int i = 1; i < n; ++i) { stL[i + 1] = edL[i + 1] = stL[i] + degL[i]; stR[i + 1] = edR[i + 1] = stR[i] + degR[i]; } for (int i = 1; i < n; ++i) { auto [u, v] = edges[i]; L[edL[v]++] = {u, i}; R[edR[u]++] = {v, i}; } for (int u = 1; u <= n; ++u) { sort(L + stL[u], L + edL[u]); sort(R + stR[u], R + edR[u]); } for (int i = 1; i < n; ++i) { auto [u, v] = edges[i]; posR[i] = lower_bound(R + stR[u], R + edR[u], pii{v, 0}) - R - stR[u]; posL[i] = lower_bound(L + stL[v], L + edL[v], pii{u, 0}) - L - stL[v]; if (edR[u] - stR[u] <= edL[v] - stL[v]) { pos[i].reserve(posR[i]); for (int p = 0; p < posR[i]; ++p) { auto [x, id] = R[stR[u] + p]; pos[i].emplace_back(lower_bound(L + stL[v], L + edL[v], pii{x, 0}) - L - stL[v]); } } else { pos[i].reserve(edL[v] - stL[v] - posL[i] - 1); for (int p = posL[i] + 1; p < edL[v] - stL[v]; ++p) { auto [x, id] = L[stL[v] + p]; pos[i].emplace_back(lower_bound(R + stR[u], R + edR[u], pii{x, 0}) - R - stR[u]); } } } vector<mint> f(n + 1, 1), g(n, 1), h(n, 0), ng(n), nh(n), preF(n + 1); for (int len = 1; len <= m; ++len) { for (int i = 1; i <= n; ++i) preF[i] = preF[i - 1] + f[i]; cout << preF[n] << " \n"[len == m]; if (len == m) break; ranges::fill(ng, 0); ranges::fill(nh, 0); fill(df, df + edL[n], 0); for (int u = 1; u <= n; ++u) { mint pre = 0; for (int _ = stL[u]; _ < edL[u]; ++_) { auto [x, id] = L[_]; ng[id] += pre; pre += g[id]; } f[u] = preF[u] + pre; sum[stR[u]] = 0; for (int i = 0; i < edR[u] - stR[u]; ++i) { ng[R[stR[u] + i].second] += sum[stR[u] + i]; if (i + 1 < edR[u] - stR[u]) sum[stR[u] + i + 1] = sum[stR[u] + i] + g[R[stR[u] + i].second]; } } for (int i = 1; i < n; ++i) { auto [x, v] = edges[i]; ng[i] += g[i] * 2 - h[i] + preF[x]; nh[i] += h[i] * 2; if (edR[x] - stR[x] <= edL[v] - stL[v]) { int j = 0; for (int p = 0; p < posR[i]; ++p) { auto [y, id] = R[stR[x] + p]; if (posL[i] + 1 < edL[v] - stL[v]) df[stL[v] + posL[i] + 1] += g[id]; if (pos[i][j] < edL[v] - stL[v]) df[stL[v] + pos[i][j++]] -= g[id]; } } else { int j = 0; for (int p = posL[i] + 1; p < edL[v] - stL[v]; ++p) { auto [u, id] = L[stL[v] + p]; nh[id] += sum[stR[x] + posR[i]] - sum[stR[x] + pos[i][j++]]; } } } for (int u = 1; u <= n; ++u) { for (int i = 0; i < edL[u] - stL[u]; ++i) { if (i) df[stL[u] + i] += df[stL[u] + i - 1]; nh[L[stL[u] + i].second] += df[stL[u] + i]; } } ng.swap(g); nh.swap(h); } } return 0; } ``` :::