[JZOJ5135][SDOI省队集训2017]逆序对

来源:互联网 发布:淘宝优惠券群是真的吗 编辑:程序博客网 时间:2024/06/04 19:36

题目大意

有多少n的排列逆序对个数为k?

模型转化

假设有一个i的排列,插入i+1逆序对个数会增加多少?
发现会增加0~i。
因此模型转化为,n个变量,0<=xi<i
问有多少x序列,满足和为k。

生成函数意义

把第i个的生成函数写出来。
i1j=0xj=1xi1x
定义F(x)=Πni=11xi1x
那么[xk]F(x)就是答案。

FFT做法

lnF(x)=ni=1ln(1xi)ln(1x)
我们知道ln(1xi)=j>=1xijj
因此可以用调和级数即n log n复杂度内求出ln F(x)。
接着用FFT做多项式的exp。
本题模数比较诡异,要用毛爷爷FFT。

DP做法

F(x)=Πni=1(1xi)(1x)n
好好学过生成函数都知道
11xm=i>=0Ci1i+m1xi
因此
F(x)=Πni=1(1xi)i>=0Ci1i+n1xi
后面就是个组合数,因此想办法求出前面的部分。
观察它的组合意义:
有n个物品,第i个物品大小为i。
假如选了j个物品,大小和为n,会贡献(-1)^j。
就是个带符号的连续背包问题!
我们考虑代入经典DP,设f[i,j]表示i个数和为j的贡献和。
想象有i个数递增的排列着。
第一种情况,全部+1。
那么f[i][j]+=f[i-1][j-i]
第二种情况,全部+1,再在最前面添加一个1。
那么f[i][j]-=f[i-1][j-i](带符号所以这里是减号)
上面这两种情况可以造出任意互不相同的一堆递增数。
但是注意可能最大的那个会超出n,因此要除掉,有
f[i][j]+=f[i-1][j-(n+1)](带符号所以这里是加号)
于是就行了。

容斥意义

考虑容斥意义,注意我们对n个变量x有限制。
第i个限制是xi<=i-1。
我们考虑容斥,枚举至少哪些条件不满足。
例如第i个条件不满足,那么xi>=i,我们将总和减去i即变成了xi>=0。
因此考虑这个意义也能得到生成函数意义下最后需要解决的问题。

#include<cstdio>#include<algorithm>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=100000+10,maxs=450+10,mo=1000000007;int f[maxs][maxn],fac[maxn*2],inv[maxn*2];int i,j,k,l,t,n,m,s,ans;int qsm(int x,int y){    if (!y) return 1;    int t=qsm(x,y/2);    t=(ll)t*t%mo;    if (y%2) t=(ll)t*x%mo;    return t;}int C(int n,int m){    if (n<m||m<0) return 0;    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;}int main(){    freopen("inverse.in","r",stdin);freopen("inverse.out","w",stdout);    scanf("%d%d",&n,&k);    fac[0]=1;    fo(i,1,n+k) fac[i]=(ll)fac[i-1]*i%mo;    inv[n+k]=qsm(fac[n+k],mo-2);    fd(i,n+k-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;    //s=floor(sqrt(k))+1;    s=450;    f[0][0]=1;    fo(i,1,s)        fo(j,0,k){            if (j>=i) (f[i][j]-=f[i-1][j-i])%=mo;            if (j>=i) (f[i][j]+=f[i][j-i])%=mo;            if (j>=n+1) (f[i][j]+=f[i-1][j-(n+1)])%=mo;        }    fo(i,0,k){        t=0;        fo(j,0,s) (t+=f[j][i])%=mo;        (ans+=(ll)t*C(k-i+n-1,n-1)%mo)%=mo;    }    (ans+=mo)%=mo;    printf("%d\n",ans);}
原创粉丝点击