欢迎来到下蛋爷之家|

下蛋爷

园龄:4年2个月粉丝:8关注:23

CF1603E A Perfect Problem 题解

Description

称一个序列为好序列当且仅当这个序列的 max×minsum,其中 sum 是序列元素和。

给定 n,M,求长度为 n,每个数在 [1,n+1] 范围内,每个非空子序列(包含序列本身)都是好序列的整数序列个数,对 M 取模。

1n200108M109,保证 M 为素数。

Solution

容易发现可以将序列排序后转化为判断所有前缀是否合法。这样就可以暴力 dp 了,时间复杂度:O(n6)

考虑优化。下面有合法序列的几条性质:

  1. k,akk,因为如果 ak<k,则 a1ak<ka1i=1kai,矛盾了。

  2. ak=k,则 a1=a2==ak=k。因为 a1ak=ka1i=1kai,所以 a1=a2==ak=k

  3. an=n+1,则 akk+1[1,k] 合法。因为 a1an=(n+1)a1i=1nai,则 a1i=1n(aia1)i=1k(aia1),所以 a1ak(k+1)a1i=1kai


对于 an=n 的情况很容易。

对于 an=n+1,一个序列合法的条件即为:

  • 1ia1,a1ain+1

  • a1+1in,i+1ain+1

  • i=1n(aia1)a1

bi=aia1,则:

  • 1ia1,0bin+1a1

  • a1+1in,i+1a1bin+1a1

  • i=1nbia1

这样就可以 dp 了。先枚举 a1 的值,可以设 fi,j,k 填了 b 的前 i 位,和为 j,当前的最大值为 k 的方案数。

转移时可以枚举 0k1 的总个数 ib 的前 i 项的和 jk 的出现次数 cnt,则当 i+cnt+1a1kn+1a1 时,即可让 fi+cnt,j+cntk,kfi,j,k1cnt!

时间复杂度:O(n4logn),过不了。


注意到 a1 的值不会很大,并且 a1n2n。证明就考虑 a1bii=a1+1n(ia1+1)=(n+a1+3)(na1)2a1(na1),可以得到 a1n2n

这样 a1 的枚举数量就只有 O(n) 级别了。

时间复杂度:O(n3nlogn)

Code

#include <bits/stdc++.h>
// #define int int64_t
const int kMaxN = 205;
int n, mod;
int fac[kMaxN], ifac[kMaxN], f[kMaxN][kMaxN][kMaxN];
constexpr int qpow(int bs, int64_t idx = mod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % mod)
if (idx & 1)
ret = (int64_t)ret * bs % mod;
return ret;
}
inline int add(int x, int y) { return (x + y >= mod ? x + y - mod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + mod); }
inline void inc(int &x, int y) { (x += y) >= mod ? x -= mod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += mod : x; }
void prework() {
fac[0] = ifac[0] = fac[1] = ifac[1] = 1;
for (int i = 2; i <= n + 1; ++i) {
fac[i] = 1ll * i * fac[i - 1] % mod;
ifac[i] = qpow(fac[i]);
}
}
void dickdreamer() {
std::cin >> n >> mod;
prework();
int ans = 1;
for (int a1 = std::max<int>(n - 18, 1); a1 <= n; ++a1) {
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= a1; ++j)
for (int k = 0; k <= n - a1 + 1; ++k)
f[i][j][k] = 0;
for (int i = 1; i <= a1; ++i) {
f[i][0][0] = 1ll * fac[n] * ifac[i] % mod;
}
for (int k = 1; k <= n - a1 + 1; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= a1; ++j) {
for (int cnt = 0; cnt <= std::min(n - i, (a1 - j) / k); ++cnt) {
if (k >= i + cnt - a1 + 1) {
inc(f[i + cnt][j + cnt * k][k], 1ll * f[i][j][k - 1] * ifac[cnt] % mod);
}
}
}
}
}
for (int i = 0; i <= a1; ++i) inc(ans, f[n][i][n - a1 + 1]);
}
std::cout << ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
posted @   下蛋爷  阅读(12)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起