搞不懂排列计数。
一种计数方法是考虑从小往大往当前排列中插入每个数,存在一次插的位置不同那么最后的结果也不同。
那么考虑插入 i i i 时,对于 x ∈ [ 0 , i − 1 ] x\in[0,i-1] x∈[0,i−1] 均有且仅有一种方法使得逆序对个数增加 x x x。
法一:
考虑 DP,设 f i , j f_{i,j} fi,j 表示插入了 i i i 个数,已经有 j j j 个逆序对的方案数,那么有转移 f i , j = ∑ l = 0 i − 1 f i − 1 , j − l f_{i,j}=\sum\limits_{l=0}^{i-1}f_{i-1,j-l} fi,j=l=0∑i−1fi−1,j−l。
暴力转移是
O
(
n
2
)
O(n^2)
O(n2) 的,考虑使用生成函数优化。设
F
i
(
x
)
F_i(x)
Fi(x) 为
f
i
,
j
f_{i,j}
fi,j 的生成函数,那么:
F
i
(
x
)
=
(
1
+
x
+
⋯
+
x
i
−
1
)
F
i
−
1
(
x
)
F_i(x)=(1+x+\cdots+x^{i-1})F_{i-1}(x)
Fi(x)=(1+x+⋯+xi−1)Fi−1(x)
于是:
F
n
(
x
)
=
∏
i
=
1
n
(
1
+
x
+
⋯
+
x
i
−
1
)
=
∏
i
=
1
n
1
−
x
i
1
−
x
=
∏
i
=
1
n
(
1
−
x
i
)
(
1
−
x
)
n
F_n(x)=\prod_{i=1}^{n}(1+x+\cdots+x^{i-1})=\prod_{i=1}^{n}\dfrac{1-x^i}{1-x}=\dfrac{\prod\limits_{i=1}^n(1-x^i)}{(1-x)^n}
Fn(x)=i=1∏n(1+x+⋯+xi−1)=i=1∏n1−x1−xi=(1−x)ni=1∏n(1−xi)
我们要求的就是上式的
k
k
k 次项系数。这个式子为多个单项式求积,考虑先取
ln
\ln
ln 再
exp
\exp
exp:
ln
F
n
(
x
)
=
∑
i
=
1
n
ln
(
1
−
x
i
)
−
n
ln
(
1
−
x
)
\ln F_n(x)=\sum_{i=1}^n\ln(1-x^i)-n\ln(1-x)
lnFn(x)=i=1∑nln(1−xi)−nln(1−x)
其中对
ln
(
1
−
x
a
)
\ln(1-x^a)
ln(1−xa) 求导貌似是个很套路的东西:先求导再积分:
[
ln
(
1
−
x
a
)
]
′
=
−
a
x
a
−
1
1
−
x
a
=
∑
i
=
0
∞
−
a
x
a
i
+
a
−
1
ln
(
1
−
x
a
)
=
∑
i
=
0
∞
−
a
a
i
+
a
x
a
i
+
a
=
∑
i
=
1
∞
−
x
a
i
i
\begin{aligned} \ [\ln(1-x^a)]'&=\dfrac{-ax^{a-1}}{1-x^a}=\sum_{i=0}^{\infty}-ax^{ai+a-1}\\ \ln(1-x^a)&=\sum_{i=0}^{\infty}-\dfrac{a}{ai+a}x^{ai+a}=\sum_{i=1}^{\infty}-\dfrac{x^{ai}}{i} \end{aligned}
[ln(1−xa)]′ln(1−xa)=1−xa−axa−1=i=0∑∞−axai+a−1=i=0∑∞−ai+aaxai+a=i=1∑∞−ixai
那么直接对于每个
a
a
a 枚举
a
a
a 的倍数即可,时间复杂度是调和级数级别的。
最后再对 ln F n ( x ) \ln F_n(x) lnFn(x) 求 exp \exp exp 取 k k k 次项即为答案。总时间复杂度 O ( n log n ) O(n\log n) O(nlogn)。
法二:
即为求下式的解的方案数:
∑
i
=
1
n
x
i
=
k
\sum_{i=1}^n x_i=k
i=1∑nxi=k
其中需要满足
x
i
∈
[
0
,
i
−
1
]
x_i\in[0,i-1]
xi∈[0,i−1]。
这个限制不好做,考虑容斥,钦定一些位置不满足:
a
n
s
=
∑
S
⊆
{
1
,
⋯
,
n
}
(
−
1
)
∣
S
∣
×
c
(
k
−
∑
i
∈
S
i
)
ans=\sum_{S\subseteq\{1,\cdots,n\}}(-1)^{|S|}\times c\left(k-\sum_{i\in S}i\right)
ans=S⊆{1,⋯,n}∑(−1)∣S∣×c(k−i∈S∑i)
解释一下这条式子,枚举对于
i
∈
S
i\in S
i∈S 不满足限制,也就是
x
i
≥
i
x_i\geq i
xi≥i,不妨设
y
i
=
x
i
−
i
y_i=x_i-i
yi=xi−i,只需满足
y
i
≥
0
y_i\geq 0
yi≥0 即可,那么对于
i
∈
S
i\in S
i∈S 的
y
i
y_i
yi 和
i
∉
S
i\not\in S
i∈S 的
x
i
x_i
xi 的限制都只有
≥
0
\geq 0
≥0。
于是 c ( k ) c(k) c(k) 的定义也显而易见了: ∑ i = 1 n z i = k \sum\limits_{i=1}^nz_i=k i=1∑nzi=k 的方案数,其中限制只有 z i ≥ 0 z_i\geq 0 zi≥0。 c ( k ) c(k) c(k) 通过插板法容易得到。
考虑 DP,暴力的想法是考虑完 1 ∼ n 1\sim n 1∼n 中的数,选了 i i i 个不同的数( ∣ S ∣ = i |S|=i ∣S∣=i),和为 j j j( ∑ p ∈ S p = j \sum\limits_{p\in S}p=j p∈S∑p=j)的方案数,尽管 i i i 的取值范围只有 k \sqrt k k,但时间复杂度还是 O ( n k k ) O(nk\sqrt k) O(nkk),甚至比暴力还慢。
神奇的方法是使用旋转体积背包:设 g i , j g_{i,j} gi,j 表示从 1 ∼ n 1\sim n 1∼n 中选 i i i 个不同的数,他们的和为 j j j 的方案数。
每次转移可以考虑集体垫高一层,然后再看是否还要在末尾加上一列:
g
i
,
j
→
g
i
,
j
+
i
g
i
,
j
→
g
i
+
1
,
j
+
i
+
1
\begin{aligned} g_{i,j}&\to g_{i,j+i}\\ g_{i,j}&\to g_{i+1,j+i+1} \end{aligned}
gi,jgi,j→gi,j+i→gi+1,j+i+1
注意对于算出来的
g
i
,
j
g_{i,j}
gi,j 要减掉
g
i
−
1
,
j
−
(
n
+
1
)
g_{i-1,j-(n+1)}
gi−1,j−(n+1),这是为了防止选的数大于
n
n
n 而减掉垫高一层后最高一列为
n
+
1
n+1
n+1 后面的列高度
≤
n
\leq n
≤n 的方案(
g
i
,
j
g_{i,j}
gi,j 一定是由某个状态垫高一层转移过来,而转移过来的状态已经保证了所有数小于等于
n
n
n)。
时间复杂度 O ( k k ) O(k\sqrt k) O(kk)。
#include<bits/stdc++.h>
#define SN 450
#define N 100010
using namespace std;
namespace modular
{
const int mod=1000000007;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return 1ll*x*y%mod;}
inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
}using namespace modular;
inline int poww(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans=mul(ans,a);
a=mul(a,a);
b>>=1;
}
return ans;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int n,k,g[SN][N];
int fac[N<<1],ifac[N<<1];
int C(int n,int m)
{
return mul(mul(fac[n],ifac[m]),ifac[n-m]);
}
int calc(int s)
{
return C(n+s-1,n-1);
}
int main()
{
n=read(),k=read();
fac[0]=1;
for(int i=1;i<=n+k;i++) fac[i]=mul(fac[i-1],i);
ifac[n+k]=poww(fac[n+k],mod-2);
for(int i=n+k;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
g[0][0]=g[1][1]=1;
for(int i=1;i<=n&&i*(i+1)/2<=k;i++)
{
for(int j=i*(i+1)/2;j<=k;j++)
{
if(j>=n+1) Dec(g[i][j],g[i-1][j-n-1]);
if(j+i<=k) Add(g[i][j+i],g[i][j]);
if(j+i+1<=k) Add(g[i+1][j+i+1],g[i][j]);
}
}
int ans=0;
for(int i=0;i<=n&&i*(i+1)/2<=k;i++)
{
for(int j=i*(i+1)/2;j<=k;j++)
{
if(i&1) Dec(ans,mul(g[i][j],calc(k-j)));
else Add(ans,mul(g[i][j],calc(k-j)));
}
}
printf("%d\n",ans);
return 0;
}