2018 Multi-University Training Contest 2 02

题意

对于 \(n,\ a_1,\ a_2,\ ...,\ a_n\),定义 \(l_i\ =\ max\{j\ |\ j\ =\ 0\ or\ a_j\ >\ a_i\},\ r_i\ =\ min\{j\ |\ j\ =\ n\ +\ 1\ or\ a_j\ >\ a_i\}\)
\(n,\ x\) 问有多少个排列 \(p_1,\ p_2,\ ...,\ p_n\) 满足 \(\sum_{i\ =\ 1}^n\ min(i\ -\ l_i,\ r_i\ -\ i)\ =\ x\),模 \(Mod\) 输出。

\(1\ \leq\ n\ \leq\ 200,\ 1\ \leq\ x\ \leq\ 10^9,\ 10^8\ \leq\ Mod\ \leq\ 10^9,\ Mod\) 是质数

做法1

考虑第 \(n\) 个数的位置,有 \(dp(n,\ x)\ =\ \sum_{i\ =\ 1,\ y}^n\ dp(i\ -\ 1,\ y)\ *\ dp(n\ -\ i,\ x\ -\ min(i,\ n\ +\ 1\ -\ i)\ -\ y)\ *\ \binom{n\ -\ 1}{i\ -\ 1}\)
\(F_n(x)\ =\ \sum_{i\ \geq\ 0}\ dp(n,\ i)\ x^i\)。发现 \(F_n(x)\) 上界 \(O(n\ log\ n)\) 级别。
直接fft复杂度太大,有个技巧,对所有 \(F_i(x)\) 求出其在 \(0,\ 1,\ ...,\ A\) 的点值,其中 \(A\)\(F_{200}(x)\) 的次数,然后进行转移。
询问时插值。插值时用拉格朗日插值+多项式除法。时间复杂度 \(O(n^3\ log\ n\ +\ T\ n^2)\)

代码

#include <bits/stdc++.h>

#ifdef DEBUG
#define debug(...) fprintf(stderr, __VA_ARGS__)
#else
#define debug(...)
#endif

#ifdef __WIN32
#define LLFORMAT "I64"
#else
#define LLFORMAT "ll"
#endif

using namespace std;

const int maxn = 210, maxm = 740;

int mod, f[maxn][maxm], pw[maxm][maxn], c[maxn][maxn], n, x, mx[maxn], z[maxm], y[maxm], inv[maxm];

inline int pow_mod(int x, int n = mod - 2) { int y = 1; while(n) { if(n & 1) y = (long long) y * x % mod; n >>= 1; x = (long long) x * x % mod; } return y; }

int main() {
	scanf("%d", &mod);
	for (int x = 0; x < maxm; ++x) for (int i = pw[x][0] = 1; i < maxn; ++i) pw[x][i] = (long long) pw[x][i - 1] * x % mod;
	for (int x = 0; x < maxm; ++x) inv[x] = pow_mod(x);
	for (int x = 0; x < maxm; ++x) f[0][x] = 1; mx[0] = 0;
	for (int i = 0; i < maxn; ++i) for (int j = c[i][0] = 1; j <= i; ++j) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
	for (int n = 1; n <= 200; ++n) {
		for (int i = 1; i <= n; ++i) {
			if(i > n - i) { for (int x = 0; x < maxm; ++x) f[n][x] = (f[n][x] << 1) % mod; }
			if(i <= n - i + 1) {
				int j;
				mx[n] = max(mx[i - 1] + mx[n - i] + (j = min(i, n + 1 - i)), mx[n]);
				for (int x = 0; x < maxm; ++x) {
					f[n][x] = ((long long) f[i - 1][x] * f[n - i][x] % mod * pw[x][j] % mod * c[n - 1][i - 1] + f[n][x]) % mod;
				}
			}
			if(i > n - i) break;
		}
	}
	while(scanf("%d%d", &n, &x) == 2) {
		int ans = 0, sz = 0; z[0] = 1;
		for (int j = 0; j <= mx[n]; ++j) {
			for (int k = 0; k <= sz; ++k) y[k + 1] = z[k]; y[0] = 0;
			z[++sz] = 0;
			for (int k = 0; k <= sz; ++k) z[k] = ((long long) y[k] - (long long) j * z[k]) % mod;
		}
		if(x >= sz) { puts("0"); continue; }
		for (int i = 0; i <= mx[n]; ++i) {
			int coef = f[n][i], t = -inv[i];
			for (int j = 0; j <= mx[n]; ++j) if(i != j) coef = (long long) coef * (i - j >= 0 ? inv[i - j] : -inv[j - i]) % mod;
			y[0] = (long long) z[0] * t % mod;
			for (int j = 1; j < sz; ++j) {
				y[j] = ((long long) ((z[j] - (long long) y[j - 1]) % mod) * t) % mod;
			}
			ans = ((long long) coef * y[x] + ans) % mod;
		}
		printf("%d\n", (ans + mod) % mod);
	}
	return 0;
}

posted @ 2018-09-09 15:54  King_George  阅读(109)  评论(0编辑  收藏  举报