题解 P8005 【An Extra Requirement】
考虑什么样的排列是合法的。不妨设
引理 :设
l 为最小的使得p_1 \sim p_t 为1 \sim t 的排列的t ,r 为最大的使得p_t \sim p_n 为t \sim n 的排列的t ,那么排列p 合法的充分必要条件是r - l \leq 1 。
我们称
证明:必要性是显然的,假设
对于
对于前缀,我们找到最大的
- 若
p_j > p_1 ,那么由1 < j < i ,p_j > p_1 = \max\{p_1,p_i\} 可知p_j 可被p_1,p_i 删掉。 - 若
p_j < p_1 ,那么由1 < j < n ,p_j < p_1 = \min\{p_1,p_n\} 可知p_j 可被p_1,p_n 删掉。
因此,
证明:对于一个
现在考虑如何计数。设
朴素地计算是
考虑
那么即有:
多项式求逆即可
现在考虑如何计算答案,我们还是容斥,计算出不合法的排列个数然后减掉。由于
对于固定的
后面是一个卷积的形式,令
因此对于固定
现在我们换一种思考的角度:考虑固定
这也是一个卷积的形式,可以用 NTT
对于
然而我的 NTT 常数实在太大了,当
Upd:这份代码里的一些点值是可以预处理的,这样就不用每次都 NTT 算一遍了,但大概还是需要
int Q, N, M, K, Lim, L, f[MN], g[MN], A[MN], B[MN], G[MN], F[MN], rev[MN], Mx, Frac[MN];
inline int qPow(int a, int b = Mod - 2) {
int res = 1;
while (b) {
if (b & 1) res = res * a % Mod;
a = a * a % Mod, b >>= 1;
}
return res;
}
inline void NTT(int *a, int Ty) {
for (int i = 0; i < Lim; i++) {
if (i < rev[i]) swap(a[i], a[rev[i]]);
}
for (int i = 1; i < Lim; i <<= 1) {
int rt = qPow(3, (Mod - 1) / (i << 1));
if (Ty == -1) rt = qPow(rt);
for (int j = 0; j < Lim; j += (i << 1)) {
int w = 1;
for (int k = 0; k < i; k++, w = w * rt % Mod) {
int p = a[j + k], q = w * a[j + k + i] % Mod;
a[j + k] = (p + q) % Mod, a[j + k + i] = ((p - q) % Mod + Mod) % Mod;
}
}
}
if (Ty == -1) {
int Inv = qPow(Lim);
for (int i = 0; i < Lim; i++) a[i] = (a[i] * Inv % Mod + Mod) % Mod;
}
}
int Inv[MN];
inline void GetInv(int *f, int *g, int Len) {
if (Len == 1) return g[0] = qPow(f[0]), void();
GetInv(f, g, (Len + 1) >> 1);
for (Lim = 1, L = 0; Lim < (Len << 1); Lim <<= 1) L++;
for (int i = 1; i < Lim; i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (L - 1));
for (int i = 0; i < Len; i++) Inv[i] = f[i];
for (int i = Len; i < Lim; i++) Inv[i] = 0;
NTT(Inv, 1), NTT(g, 1);
for (int i = 0; i < Lim; i++) g[i] = ((2 - g[i] * Inv[i] % Mod) + Mod) % Mod * g[i] % Mod;
NTT(g, -1);
for (int i = Len; i < Lim; i++) g[i] = 0;
}
#define PI3 pair <pii, int>
#define mp3(x, y, z) mp(mp(x, y), z)
PI3 q[MN]; int Ans[MN];
inline void Work() {
Mx = 1e5;
Frac[0] = 1;
for (int i = 1; i <= 2 * Mx; i++) Frac[i] = Frac[i - 1] * i % Mod;
f[1] = 1, f[0] = 1;
for (int i = 2; i <= Mx; i++) f[i] = f[i - 1] * i % Mod;
GetInv(f, g, Mx);
f[0]--;
NTT(f, 1), NTT(g, 1);
for (int i = 0; i < Lim; i++) f[i] = f[i] * g[i] % Mod;
NTT(f, -1);
// for (int i = 0; i < 10; i++) printf("%lld%c", f[i], " \n"[i == 9]);
for (int i = Mx + 1; i < Lim; i++) f[i] = g[i] = 0;
for (int i = 0; i <= Mx; i++) g[i] = f[i];
NTT(f, 1), NTT(g, 1);
for (int i = 0; i < Lim; i++) f[i] = f[i] * g[i] % Mod;
NTT(f, -1);
// for (int i = 0; i < 10; i++) printf("%lld%c", f[i], " \n"[i == 9]);
int B = 1300;
sort(q + 1, q + Q + 1, [&](const PI3 &i, const PI3 &j) { return i.fi.se < j.fi.se; });
int o = 1, k = -1;
while (o <= Q) {
k++;
int nB = k * B;
if (!nB) nB = 1;
for (int i = 0; i < Lim; i++) g[i] = A[i] = G[i] = F[i] = 0;
for (int i = 1; i <= Mx; i++) g[i] = f[i];
for (int i = 0; i <= Mx; i++) A[i] = Frac[nB + i - 1];
NTT(g, 1), NTT(A, 1);
for (int i = 0; i < Lim; i++) G[i] = g[i] * A[i] % Mod;
NTT(G, -1);
for (int i = 0; i < Lim; i++) g[i] = A[i] = 0;
for (int i = 1; i < nB; i++) g[i] = f[i];
for (int i = 1; i <= Mx; i++) A[i] = Frac[i - 1];
NTT(g, 1), NTT(A, 1);
for (int i = 0; i < Lim; i++) F[i] = g[i] * A[i] % Mod;
NTT(F, -1);
// for (int i = 0; i < 10; i++) printf("%lld%c", g[i], " \n"[i == 9]);
// for (int i = 0; i < 10; i++) printf("%lld%c", F[i], " \n"[i == 9]);
while (q[o].fi.se < B * (k + 1) && o <= Q) {
int c = q[o].fi.se - nB, d = nB, w = G[q[o].fi.fi - nB] + F[q[o].fi.fi];
while (c--) {
w = (w - Frac[d - 1] * f[q[o].fi.fi - d] % Mod + Mod) % Mod;
w = (w + Frac[q[o].fi.fi - d - 1] * f[d] % Mod) % Mod;
d++;
}
Ans[q[o].se] = (Frac[q[o].fi.fi - 1] - w % Mod + Mod) % Mod;
o++;
}
}
}
signed main(void) {
Q = read();
for (int i = 1; i <= Q; i++) q[i].fi.fi = read(), q[i].fi.se = read(), q[i].se = i;
Work();
for (int i = 1; i <= Q; i++) printf("%lld\n", Ans[i]);
return 0;
}