题解:P12032 [USACO25OPEN] Lazy Sort P

· · 题解

做法

考虑先把 B=\operatorname{sorted}(A)A 条件表示出来。

先分析 n=2 的情况。此时只有 a_1\leq a_2 或者 a_1=a_2+1 的情况才是合法的。就是不需要操作或者一次操作可以交换。

同理扩展一下,这里我是手玩了几个然后猜猜猜猜出来的。相当于问 A 能否被分成若干个区间,满足区间内极差不超过 1,并且上一个区间的 \max \leq 这个区间的 \min。就是区间内能够被排好,不同区间也不影响。

再分析一下这个东西,就是设 p_i=\max\limits_{j=1}^i a_i,需要满足 p_i\leq a_i+1

然后有一个显然的 \mathcal O(nV^2) 的 DP,设 f_{i,j} 表示前 i 个数 p_i=j 的方案数,转移枚举第 i 个数填什么,判断是否能成为前缀 \max

这个 DP 再套一个前缀和优化可以做到 \mathcal O(nV),我不知道怎么继续优化了,但是可以验证猜的结论。

然后我就因为不会了在机房被嘲笑了一上午(恼)。

接下来我们考虑如果知道了 p 的序列能还原出多少个 a

考虑 p 的断层数量 c,就是前缀 \max 的数量。设一共有 c 个断层,那么有 n-c 个位置是可以任选 p_ip_i-1 的。这里贡献是 2^{n-c}

但是这里我们只知道 p 的首尾项,具体的断层数量 c 我们要枚举。

除了 2^{n-c} 的贡献,剩下的就是 \binom{L-1}{c-1}\times\binom{V}{c-2} 即前缀 \max 的出现的方案。L 是长度,V 是可选的值域范围。由于首尾确定,所以只有 c-2 个可以自选。

这里 V 可能很大,但是枚举的下指标 c 是连续的,组合数可以从 \binom{V}{0} 开始直接暴力算,每次算一下 \binom{V}{c-1}\binom{V}{c} 的差的贡献。

这样我们就解决了 q=2 时的问题。

其他情况相当于若干个 q=2 合并起来。

f_{i,j\in\{0,1\}} 表示 p_i=a_i+j 时的方案数。转移就是 f_{i,0}=f_{j,0}\times c(0,0)+f_{j,1}\times c(1,0)f_{i,1}=f_{j,0}\times c(0,1)+f_{j,1}\times c(1,1)

求出这个系数 c 即可。

令长度为 L,首项为 x,末项为 yp,可以生成出的合法的 a 的个数为 F(L,x,y)

设此时有 len=c_{i}-c_{i-1}+1,x=v_{i-1},y=v_i

则有:

\begin{aligned} c(0, 0) &= F(L, x, y) - F(L - 1, x, y)\\ c(0, 1) &= F(L - 1, x, y + 1)\\ c(1, 0) &= F(L, x + 1, y) - F(L - 1, x + 1, y)\\ c(1, 1) &= F(L - 1, x + 1, y + 1)\\ \end{aligned} $c(0,1)$ 考虑如果在 $c_i$ 的位置一定取不到 $p_{c_i}=y+1$,所以只能在 $[c_{i-1},c_i)$ 的位置出现了 $y+1$,区间长度小 $1$。 $c(1,0)$ 与 $c(1,1)$ 的推导类似上两个。 实现注意一下细节,因为 $n$ 很大多带个 $\log$ 可能就寄了。逆元和 $2^k$ 这种东西都要线性预处理出来。 # 代码 <https://www.luogu.com.cn/record/212386055> ```cpp #include <bits/stdc++.h> using namespace std; template <int P> class mod_int { using Z = mod_int; private: static int mo(int x) { return x < 0 ? x + P : x; } public: int x; int val() const { return x; } mod_int() : x(0) {} template <class T> mod_int(const T &x_) : x(x_ >= 0 && x_ < P ? static_cast<int>(x_) : mo(static_cast<int>(x_ % P))) {} bool operator==(const Z &rhs) const { return x == rhs.x; } bool operator!=(const Z &rhs) const { return x != rhs.x; } Z operator-() const { return Z(x ? P - x : 0); } Z pow(long long k) const { Z res = 1, t = *this; while (k) { if (k & 1) res *= t; if (k >>= 1) t *= t; } return res; } Z &operator++() { x < P - 1 ? ++x : x = 0; return *this; } Z &operator--() { x ? --x : x = P - 1; return *this; } Z operator++(int) { Z ret = x; x < P - 1 ? ++x : x = 0; return ret; } Z operator--(int) { Z ret = x; x ? --x : x = P - 1; return ret; } Z inv() const { return pow(P - 2); } Z &operator+=(const Z &rhs) { (x += rhs.x) >= P && (x -= P); return *this; } Z &operator-=(const Z &rhs) { (x -= rhs.x) < 0 && (x += P); return *this; } Z operator-() { return -x; } Z &operator*=(const Z &rhs) { x = 1ULL * x * rhs.x % P; return *this; } Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); } #define setO(T, o) \ friend T operator o(const Z &lhs, const Z &rhs) \ { \ Z res = lhs; \ return res o## = rhs; \ } setO(Z, +) setO(Z, -) setO(Z, *) setO(Z, /) #undef setO friend istream & operator>>(istream &is, mod_int &x) { long long tmp; is >> tmp; x = tmp; return is; } friend ostream &operator<<(ostream &os, const mod_int &x) { os << x.val(); return os; } }; #define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++ char buf[1000000], *p1 = buf, *p2 = buf; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -f; for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T, typename... Args> void read(T &x, Args &...y) { read(x); read(y...); } const int P = 1000000007; using Z = mod_int<P>; int n, q; int l, r; int x, y; Z ans = 1; Z f[2]; Z g[2]; Z inv[5000020]; Z pw[5000020]; struct binom { int n, i; Z r; void init(int m) { n = m; i = 0; r = 1; } Z C(int j) { if (n < 0 || j < 0 || n < j) return 0; while (i + 1 <= j) { i++; r *= n - i + 1; r *= inv[i]; } return r; } }; Z calc(int len, int x, int y) { if (x > y) return 0; if (len == 1 && x != y) return 0; if (x == y) return pw[len - 1]; Z s = 0; binom C1, C2; C1.init(len - 1); C2.init(y - x - 1); for (int c = 2; c <= min(y - x + 1, len); c++) { s += C1.C(c - 1) * C2.C(c - 2) * pw[len - c]; } return s; } int main() { read(n, q); inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = inv[P % i] * (P - P / i); pw[0] = 1; for (int i = 1; i <= n; i++) pw[i] = pw[i - 1] * 2; l = -1; f[0] = 1; while (q--) { read(r, y); if (~l) { memcpy(g, f, sizeof(g)); memset(f, 0, sizeof(f)); Z c00 = calc(r - l + 1, x, y) - calc(r - l, x, y); Z c01 = calc(r - l, x, y + 1); Z c10 = calc(r - l + 1, x + 1, y) - calc(r - l, x + 1, y); Z c11 = calc(r - l, x + 1, y + 1); f[0] += g[0] * c00; f[0] += g[1] * c10; f[1] += g[0] * c01; f[1] += g[1] * c11; } l = r; x = y; } cout << f[0] + f[1] << '\n'; return 0; } ```