【LOJ6077】【2017 山东一轮集训 Day7】逆序对(排列计数DP,容斥,旋转体积背包)
搞不懂排列计数。
一种计数方法是考虑从小往大往当前排列中插入每个数,存在一次插的位置不同那么最后的结果也不同。
那么考虑插入 \(i\) 时,对于 \(x\in[0,i-1]\) 均有且仅有一种方法使得逆序对个数增加 \(x\)。
法一:
考虑 DP,设 \(f_{i,j}\) 表示插入了 \(i\) 个数,已经有 \(j\) 个逆序对的方案数,那么有转移 \(f_{i,j}=\sum\limits_{l=0}^{i-1}f_{i-1,j-l}\)。
暴力转移是 \(O(n^2)\) 的,考虑使用生成函数优化。设 \(F_i(x)\) 为 \(f_{i,j}\) 的生成函数,那么:
于是:
我们要求的就是上式的 \(k\) 次项系数。这个式子为多个单项式求积,考虑先取 \(\ln\) 再 \(\exp\):
其中对 \(\ln(1-x^a)\) 求导貌似是个很套路的东西:先求导再积分:
那么直接对于每个 \(a\) 枚举 \(a\) 的倍数即可,时间复杂度是调和级数级别的。
最后再对 \(\ln F_n(x)\) 求 \(\exp\) 取 \(k\) 次项即为答案。总时间复杂度 \(O(n\log n)\)。
法二:
即为求下式的解的方案数:
其中需要满足 \(x_i\in[0,i-1]\)。
这个限制不好做,考虑容斥,钦定一些位置不满足:
解释一下这条式子,枚举对于 \(i\in S\) 不满足限制,也就是 \(x_i\geq i\),不妨设 \(y_i=x_i-i\),只需满足 \(y_i\geq 0\) 即可,那么对于 \(i\in S\) 的 \(y_i\) 和 \(i\not\in S\) 的 \(x_i\) 的限制都只有 \(\geq 0\)。
于是 \(c(k)\) 的定义也显而易见了:\(\sum\limits_{i=1}^nz_i=k\) 的方案数,其中限制只有 \(z_i\geq 0\)。\(c(k)\) 通过插板法容易得到。
考虑 DP,暴力的想法是考虑完 \(1\sim n\) 中的数,选了 \(i\) 个不同的数(\(|S|=i\)),和为 \(j\)(\(\sum\limits_{p\in S}p=j\))的方案数,尽管 \(i\) 的取值范围只有 \(\sqrt k\),但时间复杂度还是 \(O(nk\sqrt k)\),甚至比暴力还慢。
神奇的方法是使用旋转体积背包:设 \(g_{i,j}\) 表示从 \(1\sim n\) 中选 \(i\) 个不同的数,他们的和为 \(j\) 的方案数。
每次转移可以考虑集体垫高一层,然后再看是否还要在末尾加上一列:
注意对于算出来的 \(g_{i,j}\) 要减掉 \(g_{i-1,j-(n+1)}\),这是为了防止选的数大于 \(n\) 而减掉垫高一层后最高一列为 \(n+1\) 后面的列高度 \(\leq n\) 的方案(\(g_{i,j}\) 一定是由某个状态垫高一层转移过来,而转移过来的状态已经保证了所有数小于等于 \(n\))。
时间复杂度 \(O(k\sqrt k)\)。
#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;
}

浙公网安备 33010602011771号