题目大意
给定n,k,请求出长度为n的逆序对数恰好为
答案对109+7取模。
1≤n,k≤105,1≤k≤(n2)
题目分析
首先问题可以转化成,你有n个变量
你要计算出使得∑ni=1ai=k成立的取值方案。
这个怎么计算呢?有下面两种方法,不过其实殊途同归。
容斥原理
考虑使用容斥,我们限制一些ai≥i。
设我们限制的ai≥i的i之和为
虽然总的方案有2n种,但是实际上,如果我限制的ai的i之和超过了
生成函数
题目实际上是求
我们可以枚举j,用组合数计算出相应的系数,然后计算前面的累乘里面相应的
累乘里面的式子是有组合意义的,与上面容斥原理推出来的一致。
计算方案数
于是现在的问题就变成了怎么计算在1至
脑补一下这样一个构造过程:一开始什么数都没有。对已有的数,我们有两种操作,一种是全部加1,一种是全部加
于是我们就可以写出这样的方程:设
注意到第一维我最多能够选择的数的个数显然是O(k√)级别的,因此时空复杂度是O(kk√)的。
于是本题就完美解决了。
代码实现
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int P=1000000007;
const int N=100050;
const int L=N<<1;
const int M=448;
int fact[L],invf[L];
int n,K,l,m,ans;
int f[M][N];
int quick_power(int x,int y)
{
int ret=1;
for (;y;y>>=1,x=1ll*x*x%P) if (y&1) ret=1ll*ret*x%P;
return ret;
}
void pre()
{
fact[0]=1;
for (int i=1;i<=l;++i) fact[i]=1ll*fact[i-1]*i%P;
invf[l]=quick_power(fact[l],P-2);
for (int i=l;i>=1;--i) invf[i-1]=1ll*invf[i]*i%P;
}
int C(int n,int m){return 1ll*fact[n]*invf[m]%P*invf[n-m]%P;}
void dp()
{
f[0][0]=1;
for (int i=1;i<=m;++i)
for (int j=0;j<=K;++j)
{
if (j>=i) f[i][j]=(f[i][j-i]+f[i-1][j-i])%P;
if (j>=n+1) f[i][j]=(f[i][j]-f[i-1][j-(n+1)]+P)%P;
}
}
void calc()
{
ans=0;
for (int i=0;i<=m;++i)
for (int k=0;k<=K;++k)
(ans+=(((i&1)?-1ll:1ll)*f[i][k]*C(K-k+n-1,n-1)%P+P)%P)%=P;
}
int main()
{
freopen("inverse.in","r",stdin),freopen("inverse.out","w",stdout);
scanf("%d%d",&n,&K),l=n+K,m=trunc(sqrt(K*2-1)),pre(),dp(),calc(),printf("%d\n",ans);
fclose(stdin),fclose(stdout);
return 0;
}


被折叠的 条评论
为什么被折叠?



