LOJ 6077「2017 山东一轮集训 Day7」逆序对

加入第 \(i\) 个数字的时候,新增的逆序对个数为 \([0,i-1]\),于是题意转化为:求方程 \(\sum \limits _{i=1} ^n x_i=k\ (1\leq x_i\leq i)\) 解的个数。

当没有 \((x_i\leq i)\) 的限制时,解的个数为 \(\binom {k+n-1} {n-1}\),可以使用隔板法计算。

考虑使用容斥去掉限制。我们钦点其中若干个 \(x_i\) 不满足限制,即 \(x_i>i\),剩下的限制为 \(x_i>0\),可以将 \(k\) 减去 \(i\) 使得对于所有的 \(x\) 限制均为 \(x_i>0\)。枚举 \(i\) 个限制不满足,这些位置为 \(p_1,p_2\cdots p_n\),那么答案可以写成:

\[\sum _ {i=1} ^ {n} (-1)^i\sum _{p_1<p_2<\cdots<p_i} \binom {k+n-1-\sum _{j=1}^ip_j} {n-1} \]

发现后面的式子对答案的贡献只和 \(\sum p_i\) 有关,和 \(p\) 具体是什么数没有关系。于是我们可以考虑枚举 \(s=\sum p_i\) ,求出 \(f_{i,s}\) 表示从 \([1,n]\) 中选出 \(i\) 个不同的数,和为 \(s\) 的方案数。

这是经典的整数划分问题。假设当前序列中有 \(i\) 个数,和为 \(j\) ,可以执行以下操作:

  • 将序列所有数都加 \(1\)
  • 将序列所有数都加 \(1\),并在序列最后加一个 \(1\)

可以证明,这样操作和每个整数划分的方案一一对应。

但是这样还有一个问题,序列中的数有可能会大于 \(n\)。容易发现这个大于 \(n\) 的数只有一个且必定为 \(n+1\)。这种方案数必定和去掉 \(n+1\) 后的合法序列方案数相同。

可以写出 \(dp\) 转移方程:

\[f[i][j]=f[i][j-i]+f[i-1][j-i]-f[i-1][j-n-1] \]

带上容斥系数就是:

\[f[i][j]=f[i][j-i]-f[i-1][j-i]+f[i-1][j-n-1] \]

注意到 \(i\)\(O(\sqrt k)\) 级别的,总复杂度为 \(O(n\sqrt k)\)

代码

#include <bits/stdc++.h>
using namespace std;
#define N 200010
#define Mod 1000000007
inline int read() {
    int x=0;
    char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x;
}
int fac[N],inv[N];
inline void init(int n) {
    fac[0]=fac[1]=inv[0]=inv[1]=1;
    for (int i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%Mod;
    for (int i=2;i<=n;i++) inv[i]=1LL*(Mod-Mod/i)*inv[Mod%i]%Mod;
    for (int i=2;i<=n;i++) inv[i]=1LL*inv[i-1]*inv[i]%Mod;
}
inline int C(int n,int m) {
    return 1LL*fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}
int dp[510][N],f[N];
int main() {
    int n=read(),k=read(),ans=0; init(n+k);
    int m=min(n,(int)sqrt(k<<1)+1); dp[0][0]=f[0]=1;
    for (int i=1;i<=m;i++)
        for (int j=i*(i+1)>>1;j<=k;j++) {
            (dp[i][j]+=dp[i][j-i])%=Mod;
            (dp[i][j]+=Mod-dp[i-1][j-i])%=Mod;
            if (j>=n+1) (dp[i][j]+=dp[i-1][j-n-1])%=Mod;
            (f[j]+=dp[i][j])%=Mod;
        }
    for (int i=0;i<=k;i++) (ans+=1LL*f[i]*C(k-i+n-1,n-1)%Mod)%=Mod;
    return printf("%d\n",ans),0;
}
posted @ 2020-10-09 07:15  bo1949  阅读(146)  评论(0)    收藏  举报