题目:
https://loj.ac/problem/6077
给定 n , k n,k n,k ,请求出长度为 n n n的逆序对数恰好为 k k k的排列的个数。答案对 1 0 9 + 7 10^9+7 109+7取模。
思路1:
设
a
i
a_i
ai为数字
i
i
i对逆序数的贡献,则
0
≤
a
i
≤
i
−
1
0\le a_i\le i-1
0≤ai≤i−1。并且只要所有
a
i
a_i
ai满足该条件,都一定能构造出唯一的排列。
所以问题变成有多少方案使得
∑
i
=
1
n
a
i
=
k
,
0
≤
a
i
≤
i
−
1
\sum_{i=1}^{n}a_i=k,0\le a_i\le i-1
∑i=1nai=k,0≤ai≤i−1成立。考虑生成函数。
F
(
x
)
=
1
(
1
+
x
)
(
1
+
x
+
x
2
)
.
.
.
(
1
+
x
+
x
2
+
.
.
.
x
n
−
1
)
=
1
−
x
1
−
x
1
−
x
2
1
−
x
1
−
x
3
1
−
x
.
.
.
1
−
x
n
1
−
x
=
∏
i
=
1
n
(
1
−
x
i
)
(
1
−
x
)
n
=
∏
i
=
1
n
(
1
−
x
i
)
∗
∑
j
=
0
∞
(
j
+
n
−
1
j
)
x
j
\begin{aligned} F(x)&=1(1+x)(1+x+x^2)...(1+x+x^2+...x^{n-1})\\ &=\frac{1-x}{1-x}\frac{1-x^2}{1-x}\frac{1-x^3}{1-x}...\frac{1-x^n}{1-x}\\ &=\frac{\prod_{i=1}^{n}(1-x^i)}{(1-x)^n}\\ &=\prod_{i=1}^{n}(1-x^i)*\sum_{j=0}^{\infty}{j+n-1\choose j}x^j \end{aligned}
F(x)=1(1+x)(1+x+x2)...(1+x+x2+...xn−1)=1−x1−x1−x1−x21−x1−x3...1−x1−xn=(1−x)n∏i=1n(1−xi)=i=1∏n(1−xi)∗j=0∑∞(jj+n−1)xj
我们只要求出最后
x
k
x^k
xk的系数就行,主要就是展开
∏
i
=
1
n
(
1
−
x
i
)
\prod_{i=1}^{n}(1-x^i)
∏i=1n(1−xi),这个问题等价于背包,不过算系数的时候还要把符号考虑进去。
x
i
x^i
xi的系数等于
∑
j
(
−
1
)
j
f
(
j
,
i
)
\sum_{j}(-1)^jf(j,i)
j∑(−1)jf(j,i)
最后就是算一个卷积。
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200009,M=450;
int n,k,Max;
ll dp[M][N],p[N],p1[N],ans=0;
ll qpow(ll a,ll b){
ll res=1;
a%=mod;
while(b){
if(b&1)
res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll C(int a,int b){
if(a<0||b<0||a<b)
return 0;
return p[a]*p1[b]%mod*p1[a-b]%mod;
}
ll cal(int x){
ll res=0;
for(int i=0;i<=Max;i++){
if(i&1)//符号
res=(res-dp[i][x]+mod)%mod;
else
res=(res+dp[i][x])%mod;
}
return res;
}
int main() {
cin>>n>>k;
Max=(int)ceil(sqrt(2*k));
p[0]=1;
for(int i=1;i<=n+k;i++)
p[i]=p[i-1]*i%mod;
p1[n+k]=qpow(p[n+k],mod-2);
for(int i=n+k-1;i>=0;i--)
p1[i]=p1[i+1]*(i+1)%mod;
dp[0][0]=1;
for(int i=1;i<=Max;i++)
for(int j=i;j<=k;j++){
if(j>=i)
dp[i][j]=(dp[i][j]+dp[i-1][j-i]+dp[i][j-i])%mod;
if(j>n)
dp[i][j]=(dp[i][j]-dp[i-1][j-(n+1)]+mod)%mod;
}
for(int i=0;i<=k;i++)
ans=(ans+C(i+n-1,i)*cal(k-i)%mod)%mod;
cout<<ans;
return 0;
}
思路2:
∑
i
=
1
n
a
i
=
k
,
0
≤
a
i
≤
i
−
1
\sum_{i=1}^{n}a_i=k,0\le a_i\le i-1
∑i=1nai=k,0≤ai≤i−1
这个问题如果只考虑
a
i
≥
0
a_i\ge 0
ai≥0的话,那么方案数就等于
(
k
+
n
−
1
n
−
1
)
{k+n-1\choose n-1}
(n−1k+n−1),然后用容斥减掉不合法方案,设
A
i
A_i
Ai表示
a
i
≥
i
a_i\ge i
ai≥i的所有方案的集合,则
a
n
s
=
∣
A
1
‾
∩
A
2
‾
.
.
.
∩
A
n
‾
∣
=
∣
S
∣
−
∣
A
1
∪
A
2
.
.
.
∪
A
n
∣
=
∣
S
∣
−
∑
i
∣
A
i
∣
+
∑
i
,
j
∣
A
i
∩
A
j
∣
.
.
.
(
−
1
)
n
∣
A
1
∩
A
2
.
.
.
∩
A
n
∣
\begin{aligned} ans&=|\overline {A_1}\cap\overline {A_2}...\cap\overline {A_n}|\\ &=|S|-|A_1\cup A_2...\cup A_n|\\ &=|S|-\sum_{i}|A_i|+\sum_{i,j}|A_i\cap A_j|...(-1)^n|A_1\cap A_2...\cap A_n| \end{aligned}
ans=∣A1∩A2...∩An∣=∣S∣−∣A1∪A2...∪An∣=∣S∣−i∑∣Ai∣+i,j∑∣Ai∩Aj∣...(−1)n∣A1∩A2...∩An∣
后面这个可以不用真的去枚举,也是可以用背包算。