【Hackerrank 101Hack 43】【JZOJ5135】K-Inversion Permutations 题解

来源:互联网 发布:指纹 门禁锁 知乎 编辑:程序博客网 时间:2024/05/16 11:19

题目大意

      求长度为 n、逆序对数量为 k 的排列的数量。
      n,k<=1e5


【50%】n, k<=1000

      f[i][j] 表示长度为 i、逆序对数量为 j 的方案数。
      f[i][j]=i1now=0f[i1][jnow]

【100%解法1】

      考虑每个元素的贡献(该元素前面有多少个比它大),可以得到一个贡献序列 B。其中该序列满足 0<=B[i]<=i1,且 B[i]=k
      该序列与原序列唯一对应,所以我们要求有多少不同的 B 序列。

      考虑 B[i] 的生成函数: 1+x+x2+...+xi1=1xi1x,因此最后和为 k 的方案数就是 ni=11xi1xxk 的系数。

      来一个不清真的做法。

      F(x)=ni=11xi1x,则 ln F(x)=ni=1ln(1xi)n ln(1x)
      ln(1x)=(x+x22+x33+...)
      ln(1xi)=(xi+x2i2+x3i3+...)
      用调和级数那样 O(k log k) 处理 ln F(x),FFT 算 eln F(x)

      以上抄自 WerKeyTom_FTD 给的笔记,我还没学会

【100%解法2】

      来一个清真的做法。

i=1n1xi1x  =  ni=1(1xi)(1x)n  =  i=1n(1xi)×i=0n(i+n1n1)xi

      所以现在分成前面和后面两个部分,最后枚举前面是 xi,后面就是 xki,把对应系数乘起来即可。后面的系数是普通组合数,所以目标变成算前面的系数。

      前面部分可以看成:有 n 个物品,第 i 个物品大小为 i,且选了 x 个物品的话方案数要乘上 (1)x,求大小为 k 的方案数。

      这就是个经典 dp。设 f[i][j] 表示用 i 个互不相同的物品、和为 j 的方案数。我们限定这 i 个物品是有序(从小到大)的。
      转移有两种操作:一种是全体体积加 1,一种是新加一个大小为 1 的物品。(注意新加一个物品的时候,1 的指数会变,所以这时候算方案数是 f[i][j]=...。)
      这样垒上去可能会使某些物品大小超过 n,所以当某个物品超限的时候要及时减去,即 f[i][j]+=f[i1][j(n+1)]。(注意这里 1 的指数又变了,所以是加等于。)

      然后会发现物品最多只需要 k 个,所以复杂度是 O(kk)

代码

//解法2

#include<cmath>#include<cstdio>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;typedef long long LL;const int maxn=1e5+5, maxsqrtk=448;const LL mo=1e9+7;int n,k;LL fac[2*maxn],ny[2*maxn];LL mi(LL x,LL y){    LL re=1;    for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo;    return re;}void C_Pre(int n){    fac[0]=ny[0]=1;    fo(i,1,n) fac[i]=fac[i-1]*i%mo;    ny[n]=mi(fac[n],mo-2);    fd(i,n-1,1) ny[i]=ny[i+1]*(i+1)%mo;}LL C(int n,int m) {return fac[n]*ny[m]%mo*ny[n-m]%mo;}LL f[maxsqrtk+2][maxn],g[maxn];int main(){    scanf("%d %d",&n,&k);    C_Pre(2*max(n,k));    f[0][0]=g[0]=1;    fo(i,1,maxsqrtk)        fo(j,1,k)        {            if (j>=i) (f[i][j]-=f[i-1][j-i])%=mo; // 新加一个大小为1的物品            if (j>=i) (f[i][j]+=f[i][j-i])%=mo;  // 全体+1            if (j>=n+1) (f[i][j]+=f[i-1][j-(n+1)])%=mo;            (g[j]+=f[i][j])%=mo;        }    LL ans=0;    fo(i,0,k) (ans+=g[i]*C(k-i+n-1,n-1))%=mo;    printf("%lld\n",(ans+mo)%mo);}
原创粉丝点击