loj6077. 「2017 山东一轮集训 Day7」逆序对

题目描述:

loj

题解:

容斥+生成函数。

考虑加入的第$i$个元素对结果的贡献是$[0,i-1]$,我们可以列出生成函数。

长这样:$(1)*(1+x)*(1+x+x^2)*……*(1+x+x^2+……+x^{n-1})=\frac{\prod_{i=1}^{n}1-x^i}{(1-x)^n}$

把分母提出来:$\frac{1}{(1-x)^n} = (1+x+x^2+……)^n = \sum_{i=0}^{k} C_{i+n-1}^{n-1}$,日常小球放盒。

现在还剩$\prod_{i=1}^n 1-x^i$,可以考虑将该式理解为从$1$到$n$选$i$个数,总和为$j$,对该项系数贡献为$(-1)^i$。

由于从$1$到$n$不重复,可以发现$\sum_{i=1}^{447}>100000$,那么$i \le 447$。

那么就可以$dp$了。状态为$f[i][j]$,表示当前选了$i$个数总和为$j$,且最后一项不大于$n$的方案数。

要求选数不重复怎么办?

考虑将其构造成一个上升序列。我们用枚举差值的思想,保证前后差值大于$0$。

转移有三种:

1.将$i$个数集体+1,此时$f[i][j]+=f[i][j-i]$;

2.将$i$个数集体+1再在最前面加入一个1,此时$f[i][j]+=f[i-1][j-i]$;

3.考虑我们每次都让每个数+1,所以当$j>n$时会出现最后一项为$n+1$的情况,此时$f[i][j]-=f[i-1][j-n-1]$;

这样转移就可以了。

最后卷积卷出第$k$位的值就可以了。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MOD = 1000000007;
const int N = 100050;
const int M = 450;
int fastpow(int x,int y)
{
    int ret = 1;
    while(y)
    {
        if(y&1)ret=1ll*ret*x%MOD;
        x=1ll*x*x%MOD;y>>=1;
    }
    return ret;
}
template<typename T>
inline void Mod(T&x){if(x>=MOD)x-=MOD;}
int n,m;
int jc[N<<1],jny[N<<1],f[M][N];
void init()
{
    jc[0] = 1;
    for(int i=1;i<=n+m;i++)jc[i]=1ll*jc[i-1]*i%MOD;
    jny[n+m] = fastpow(jc[n+m],MOD-2);
    for(int i=n+m;i;i--)jny[i-1]=1ll*jny[i]*i%MOD;
}
int C(int x,int y){return 1ll*jc[x]*jny[y]%MOD*jny[x-y]%MOD;}

int main()
{
    scanf("%d%d",&n,&m);
    init();
    f[0][0] = 1;
    for(int i=1;i<M;i++)for(int j=i;j<=m;j++)
    {
        Mod(f[i][j] = f[i-1][j-i]+f[i][j-i]);
        if(j>n)Mod(f[i][j]+=MOD-f[i-1][j-n-1]);
    }
    int ans = 0;
    for(int i=0;i<=m;i++)
    {
        int tmp = 0;
        for(int j=0;j<M;j++)
            if(j&1)Mod(tmp+=MOD-f[j][i]);
            else Mod(tmp+=f[j][i]);
        Mod(ans+=1ll*tmp*C(n+m-i-1,n-1)%MOD);
    }
    printf("%d\n",ans);
    return 0;
}
View Code

 

posted @ 2019-07-05 07:48  LiGuanlin  阅读(...)  评论(...编辑  收藏