1 条题解
-
0
自动搬运
来自洛谷,原作者为

ღꦿ࿐
一直游到海水变蓝搬运于
2025-08-24 22:19:40,当前版本为作者最后更新于2024-06-20 23:35:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
本题存在低于 的做法。
逆序对是大小关系,我们在小的那个数处统计每对逆序对,考虑从大到小插入每一个数,这样所有数都比他大,这样它插入在第 个就会产生 个逆序对,假设现在有 个数则它可以产生 中个逆序对,且每种都恰好有一种插法。
那么我们现在可以进行一个 的 dp 了, 表示前 个数有 个逆序对的方案数,使用前缀和优化可以做到 。
重新回顾我们现在的问题:第 个数在 中任选,求所有数和为 的方案数,
考虑没有 这个限制的方案数, 个任意正整数和为 ,这是经典的插板法,答案即 。
我们考虑容斥去掉限制, 是困难的,而将其容斥为 后是简单的,我们直接从 中扣掉 即可。
钦定一些 超限了,我们只关心我们钦定的 的和为 的容斥系数之和。
问题变为对所有 求出 中选一些数 和为 的容斥系数之和,每选一个数带来 的容斥系数。
这个问题直接做似乎还是只能 。
考虑经典的性质:最多选择了 个数,否则和一定 。
证明:选择前 小的数的和是 的,所以无论如何选的个数都是 级别的。
于是我们考虑从竖着一个数一个数 dp,换成横着一层一层 dp,从大到小加入每个数 ,则 会在 个数中产生贡献,即 。(相当于给所有已经加入的数”垫高“若干并加入新的数)
让 表示目前有 个数,为 的方案数,那么我们有
分别表示:新加入一个数并往上垫高一层,往上垫高一层。
然后因为要求了每个数都不超过 ,我们减去第一个数在垫高这一层后达到 的情况,这是一个子问题,减去 后变为一个 个数和为 的方案数。
于是我们在 的时间复杂度内解决了本题。
代码:2024/6/20 提交时为最优解。
#include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define all(x) x.begin(),x.end() #define FIN(x) freopen(#x,"r",stdin) #define FOUT(x) freopen(#x,"w",stdout) #define cerr(x) cerr << #x"= " << x << "\n" #define rep(i,x,y) for(int i = (x) ; i <= (y) ; ++ i) #define Rep(i,x,y) for(int i = (x) ; i >= (y) ; -- i) #define SYNC(x) std :: ios :: sync_with_stdio (x); if (!x) {cin.tie(0);cout.tie(0);} using std :: cin , std :: cout , std :: cerr ; template<ll Mod> struct math { std :: vector <ll> fac , fiv , inv ; ll pow (ll x , ll y = Mod - 2) { ll res = 1 ; for (x %= Mod ; y ; (x *= x) %= Mod , y >>= 1) if (y & 1) (res *= x) %= Mod ; return res; } inline math () {} inline math (int n) : fac(n + 1) , fiv(n + 1) , inv(n + 1) { fac[0] = 1 ; rep (i,1,n) fac[i] = fac[i - 1] * i % Mod ; fiv[n] = pow(fac[n]) ; Rep (i,n,1) fiv[i - 1] = fiv[i] * i % Mod , inv[i] = fac[i - 1] * fiv[i] % Mod ; } inline void init (int n) { fac.resize(n + 1) , fiv.resize(n + 1) , inv.resize(n + 1) ; fac[0] = 1 ; rep (i,1,n) fac[i] = fac[i - 1] * i % Mod ; fiv[n] = pow(fac[n]) ; Rep (i,n,1) fiv[i - 1] = fiv[i] * i % Mod , inv[i] = fac[i - 1] * fiv[i] % Mod ; } inline ll binom (int n,int m) { if (n < m || m < 0) return 0; return fac[n] * fiv[m] % Mod * fiv[n - m] % Mod ; } inline ll perm (int n,int m) { if (n < m || m < 0) return 0; return fac[n] * fiv[n - m] % Mod ; } inline ll bracket (int x) { return fac[x * 2] * fiv[x] % Mod * fiv[x + 1] % Mod ; } } ; const int mod = 1e9 + 7 ; inline void inc (int &tar,int ths) { if ((tar += ths) >= mod) tar -= mod ; } inline void dec (int &tar,int ths) { if ((tar -= ths) < 0) tar += mod ; } signed main( ) { SYNC (false); int n , c ; cin >> n >> c ; math<mod> M (n + c) ; std :: vector <int> f (c + 1) ; f[0] = 1 ; ll res = M.binom (c + n - 1 , n - 1) ; for (int i = 1 ; i * (i + 1) / 2 <= c && i <= n ; ++ i) { std :: vector <int> g(c+1) ; rep (j,i,c) { g[j] = f[j - i] ; inc (g[j] , g[j - i]) ; if (j >= n + 1) dec (g[j] , f[j - n - 1]) ; } f.swap(g) ; rep (j,0,c) { (res += (i&1?mod-f[j]:f[j]) * M.binom (c - j + n - 1 , n - 1)) %= mod ; } } cout << res << '\n' ; }
更加本质的东西:
直接从生成函数和 Ferrers 图像的角度也可以得到相同复杂度的做法,上面那个容斥的本质是 ,后面的部分我们是在快速求 ,从分拆数等角度有很多 的做法。
- 1
信息
- ID
- 5359
- 时间
- 1000ms
- 内存
- 63MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者