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

来源:互联网 发布:口腔医学专升本知乎 编辑:程序博客网 时间:2024/05/29 18:59

这题(BZOJ2431的加强版)厉害了。。。

考虑从小到大加入数,则i就可以让逆序对数+[0,i-1]

问题就变为了现在有一个不定方程:x1+..+xn=m,且0<=xi<=i-1

考虑用容斥,于是要计算F(i,j)表示[1,n]选i个数和为j的方案数

则最终答案为sum{(F(0,j)-F(1,j)+F(2,j)-...)*(和为m-j有n个变量的不定方程解的个数)}(j=0..m)


如何计算F(i,j)呢?一个显然的方法是类似背包的DP,不过这题目有点特殊。可以假设你现在有一个序列,你每次可以选择把序列整体+若干1,然后再添一个1,然后再+若干1。由于可能会加到大于n,把这部分减掉就好了.

这样最多只能弄大概450次,于是复杂度就是O(450*m)


#include<bits/stdc++.h>#define mod 1000000007#define maxn 100100using namespace std;int lim,n,m,ans,f[456][maxn],fac[maxn<<1],inv[maxn<<1];int qpow(int a,int b){int ans=1,tmp=a;for(;b;b>>=1,tmp=1ll*tmp*tmp%mod)if(b&1)ans=1ll*ans*tmp%mod;return ans;}int C(int x,int y){if(x>y)return 0;return 1ll*fac[y]*inv[x]%mod*inv[y-x]%mod;}int main(){scanf("%d%d",&n,&m);fac[0]=inv[0]=1;for(int i=1;i<=n+m;++i)fac[i]=1ll*fac[i-1]*i%mod;inv[n+m]=qpow(fac[n+m],mod-2);for(int i=n+m-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;lim=sqrt(2*m)+3;f[0][0]=1;for(int i=1;i<=lim;++i)for(int j=0;j<=m;++j){if(j>=i)f[i][j]=(f[i][j-i]+f[i-1][j-i])%mod;if(j>=n+1)f[i][j]=(f[i][j]-f[i-1][j-n-1])%mod;}for(int i=0;i<=m;++i){int pans=0;for(int j=0;j<=lim;++j)if(j&1)pans=(pans-f[j][i])%mod;else pans=(pans+f[j][i])%mod;ans=(ans+1ll*pans*C(n-1,n+m-i-1))%mod;if(ans<0)ans+=mod;}printf("%d",ans);}


阅读全文
0 0
原创粉丝点击