【Hackerrank 101Hack 43】【JZOJ5135】K-Inversion Permutations 题解

题目大意

  求长度为 nn、逆序对数量为 kk 的排列的数量。
  n,k105n, k \le 10^5

【50%】n, k<=1000

  设 f[i][j]f[i][j] 表示长度为 ii、逆序对数量为 jj 的方案数。
  f[i][j]=now=0i1f[i1][jnow]f[i][j]=\sum_{now=0}^{i-1} f[i-1][j-now]

【100%解法1】

  考虑每个元素的贡献(该元素前面有多少个比它大),可以得到一个贡献序列 BB。其中该序列满足 0<=B[i]<=i10<=B[i]<=i-1,且 B[i]=k\sum B[i]=k
  该序列与原序列唯一对应,所以我们要求有多少不同的 BB 序列。

  考虑 B[i]B[i] 的生成函数: 1+x+x2+...+xi1=1xi1x1+x+x^2+...+x^{i-1}=\frac{1-x^i}{1-x},因此最后和为 kk 的方案数就是 i=1n1xi1x\prod_{i=1}^n \frac{1-x^i}{1-x}xkx^k 的系数。

  来一个不清真的做法。

  设 F(x)=i=1n1xi1xF(x)=\prod_{i=1}^n \frac{1-x^i}{1-x},则

lnF(x)=i=1nln(1xi)n ln(1x)ln(1x)=(x+x22+x33+...)ln(1xi)=(xi+x2i2+x3i3+...)\ln F(x)=\sum_{i=1}^n ln(1-x^i)-n~ln(1-x) \\ \ln(1-x)=-(x+\frac{x^2}{2}+\frac{x^3}{3}+...) \\ \ln(1-x^i)=-(x^i+\frac{x^{2i}}{2}+\frac{x^{3i}}{3}+...)

  用调和级数那样 O(k log k)O(k~log~k) 处理 ln F(x)ln~F(x),FFT 算 eln F(x)e^{ln~F(x)}

  以上抄自 WerKeyTom_FTD 给的笔记,我还没学会

【100%解法2】

  来一个清真的做法。

i=1n1xi1x=i=1n(1xi)(1x)n=i=1n(1xi)×i=0n(i+n1n1)xi\begin{aligned} &\prod_{i=1}^n \frac{1-x^i}{1-x} \\ =& \frac{\prod_{i=1}^n (1-x^i)}{(1-x)^n} \\ =& \prod_{i=1}^n (1-x^i)×\sum_{i=0}^n \dbinom{i+n-1}{n-1} x^i \end{aligned}

  所以现在分成前面和后面两个部分,最后枚举前面是 xix^i,后面就是 xkix^{k-i},把对应系数乘起来即可。后面的系数是普通组合数,所以目标变成算前面的系数。

  前面部分可以看成:有 nn 个物品,第 ii 个物品大小为 ii,且选了 xx 个物品的话方案数要乘上 (1)x(-1)^x,求大小为 kk 的方案数。

  这就是个经典 dp。设 f[i][j]f[i][j] 表示用 ii 个互不相同的物品、和为 jj 的方案数。我们限定这 ii 个物品是有序(从小到大)的。
  转移有两种操作:一种是全体体积加 11,一种是新加一个大小为 11 的物品。(注意新加一个物品的时候,1-1 的指数会变,所以这时候算方案数是 f[i][j]=...f[i][j]-=...。)
  这样垒上去可能会使某些物品大小超过 nn,所以当某个物品超限的时候要及时减去,即 f[i][j]+=f[i1][j(n+1)]f[i][j]+=f[i-1][j-(n+1)]。(注意这里 1-1 的指数又变了,所以是加等于。)

  然后会发现物品最多只需要 k\sqrt k 个,所以复杂度是 O(kk)O(k \sqrt k)

代码

//解法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; // 新加一个大小为1的物品
if (j>=i) (f[i][j]+=f[i][j-i])%=mo; // 全体+1
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);
}