题目大意
求长度为 n、逆序对数量为 k 的排列的数量。
n,k≤105
【50%】n, k<=1000
设 f[i][j] 表示长度为 i、逆序对数量为 j 的方案数。
f[i][j]=∑now=0i−1f[i−1][j−now]
【100%解法1】
考虑每个元素的贡献(该元素前面有多少个比它大),可以得到一个贡献序列 B。其中该序列满足 0<=B[i]<=i−1,且 ∑B[i]=k。
该序列与原序列唯一对应,所以我们要求有多少不同的 B 序列。
考虑 B[i] 的生成函数: 1+x+x2+...+xi−1=1−x1−xi,因此最后和为 k 的方案数就是 ∏i=1n1−x1−xi 的 xk 的系数。
来一个不清真的做法。
设 F(x)=∏i=1n1−x1−xi,则
lnF(x)=i=1∑nln(1−xi)−n ln(1−x)ln(1−x)=−(x+2x2+3x3+...)ln(1−xi)=−(xi+2x2i+3x3i+...)
用调和级数那样 O(k log k) 处理 ln F(x),FFT 算 eln F(x)
以上抄自 WerKeyTom_FTD 给的笔记,我还没学会
【100%解法2】
来一个清真的做法。
==i=1∏n1−x1−xi(1−x)n∏i=1n(1−xi)i=1∏n(1−xi)×i=0∑n(n−1i+n−1)xi
所以现在分成前面和后面两个部分,最后枚举前面是 xi,后面就是 xk−i,把对应系数乘起来即可。后面的系数是普通组合数,所以目标变成算前面的系数。
前面部分可以看成:有 n 个物品,第 i 个物品大小为 i,且选了 x 个物品的话方案数要乘上 (−1)x,求大小为 k 的方案数。
这就是个经典 dp。设 f[i][j] 表示用 i 个互不相同的物品、和为 j 的方案数。我们限定这 i 个物品是有序(从小到大)的。
转移有两种操作:一种是全体体积加 1,一种是新加一个大小为 1 的物品。(注意新加一个物品的时候,−1 的指数会变,所以这时候算方案数是 f[i][j]−=...。)
这样垒上去可能会使某些物品大小超过 n,所以当某个物品超限的时候要及时减去,即 f[i][j]+=f[i−1][j−(n+1)]。(注意这里 −1 的指数又变了,所以是加等于。)
然后会发现物品最多只需要 k 个,所以复杂度是 O(kk)
代码
//解法2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<cmath> #include<cstdio> #include<algorithm> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std;
typedef long long LL;
const int maxn=1e5+5, maxsqrtk=448; const LL mo=1e9+7;
int n,k;
LL fac[2*maxn],ny[2*maxn]; LL mi(LL x,LL y) { LL re=1; for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo; return re; } void C_Pre(int n) { fac[0]=ny[0]=1; fo(i,1,n) fac[i]=fac[i-1]*i%mo; ny[n]=mi(fac[n],mo-2); fd(i,n-1,1) ny[i]=ny[i+1]*(i+1)%mo; } LL C(int n,int m) {return fac[n]*ny[m]%mo*ny[n-m]%mo;}
LL f[maxsqrtk+2][maxn],g[maxn]; int main() { scanf("%d %d",&n,&k); C_Pre(2*max(n,k)); f[0][0]=g[0]=1; fo(i,1,maxsqrtk) fo(j,1,k) { if (j>=i) (f[i][j]-=f[i-1][j-i])%=mo; if (j>=i) (f[i][j]+=f[i][j-i])%=mo; if (j>=n+1) (f[i][j]+=f[i-1][j-(n+1)])%=mo; (g[j]+=f[i][j])%=mo; } LL ans=0; fo(i,0,k) (ans+=g[i]*C(k-i+n-1,n-1))%=mo; printf("%lld\n",(ans+mo)%mo); }
|