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

题目:
https://loj.ac/problem/6077

给定 n , k n,k n,k ,请求出长度为 n n n的逆序对数恰好为 k k k的排列的个数。答案对 1 0 9 + 7 10^9+7 109+7取模。

思路1:
a i a_i ai为数字 i i i对逆序数的贡献,则 0 ≤ a i ≤ i − 1 0\le a_i\le i-1 0aii1。并且只要所有 a i a_i ai满足该条件,都一定能构造出唯一的排列。
所以问题变成有多少方案使得 ∑ i = 1 n a i = k , 0 ≤ a i ≤ i − 1 \sum_{i=1}^{n}a_i=k,0\le a_i\le i-1 i=1nai=k,0aii1成立。考虑生成函数。
F ( x ) = 1 ( 1 + x ) ( 1 + x + x 2 ) . . . ( 1 + x + x 2 + . . . x n − 1 ) = 1 − x 1 − x 1 − x 2 1 − x 1 − x 3 1 − x . . . 1 − x n 1 − x = ∏ i = 1 n ( 1 − x i ) ( 1 − x ) n = ∏ i = 1 n ( 1 − x i ) ∗ ∑ j = 0 ∞ ( j + n − 1 j ) x j \begin{aligned} F(x)&=1(1+x)(1+x+x^2)...(1+x+x^2+...x^{n-1})\\ &=\frac{1-x}{1-x}\frac{1-x^2}{1-x}\frac{1-x^3}{1-x}...\frac{1-x^n}{1-x}\\ &=\frac{\prod_{i=1}^{n}(1-x^i)}{(1-x)^n}\\ &=\prod_{i=1}^{n}(1-x^i)*\sum_{j=0}^{\infty}{j+n-1\choose j}x^j \end{aligned} F(x)=1(1+x)(1+x+x2)...(1+x+x2+...xn1)=1x1x1x1x21x1x3...1x1xn=(1x)ni=1n(1xi)=i=1n(1xi)j=0(jj+n1)xj
我们只要求出最后 x k x^k xk的系数就行,主要就是展开 ∏ i = 1 n ( 1 − x i ) \prod_{i=1}^{n}(1-x^i) i=1n(1xi),这个问题等价于背包,不过算系数的时候还要把符号考虑进去。 x i x^i xi的系数等于
∑ j ( − 1 ) j f ( j , i ) \sum_{j}(-1)^jf(j,i) j(1)jf(j,i)
最后就是算一个卷积。

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200009,M=450;
int n,k,Max;
ll dp[M][N],p[N],p1[N],ans=0;
ll qpow(ll a,ll b){
    ll res=1;
    a%=mod;
    while(b){
        if(b&1)
            res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll C(int a,int b){
    if(a<0||b<0||a<b)
        return 0;
    return p[a]*p1[b]%mod*p1[a-b]%mod;
}
ll cal(int x){
    ll res=0;
    for(int i=0;i<=Max;i++){
        if(i&1)//符号
            res=(res-dp[i][x]+mod)%mod;
        else
            res=(res+dp[i][x])%mod;
    }
    return res;
}
int main() {
    cin>>n>>k;
    Max=(int)ceil(sqrt(2*k));
    p[0]=1;
    for(int i=1;i<=n+k;i++)
        p[i]=p[i-1]*i%mod;
    p1[n+k]=qpow(p[n+k],mod-2);
    for(int i=n+k-1;i>=0;i--)
        p1[i]=p1[i+1]*(i+1)%mod;
    dp[0][0]=1;
    for(int i=1;i<=Max;i++)
    for(int j=i;j<=k;j++){
        if(j>=i)
            dp[i][j]=(dp[i][j]+dp[i-1][j-i]+dp[i][j-i])%mod;
        if(j>n)
            dp[i][j]=(dp[i][j]-dp[i-1][j-(n+1)]+mod)%mod;
    }
    for(int i=0;i<=k;i++)
        ans=(ans+C(i+n-1,i)*cal(k-i)%mod)%mod;
    cout<<ans;
    return 0;
}

思路2:
∑ i = 1 n a i = k , 0 ≤ a i ≤ i − 1 \sum_{i=1}^{n}a_i=k,0\le a_i\le i-1 i=1nai=k,0aii1
这个问题如果只考虑 a i ≥ 0 a_i\ge 0 ai0的话,那么方案数就等于 ( k + n − 1 n − 1 ) {k+n-1\choose n-1} (n1k+n1),然后用容斥减掉不合法方案,设 A i A_i Ai表示 a i ≥ i a_i\ge i aii的所有方案的集合,则
a n s = ∣ A 1 ‾ ∩ A 2 ‾ . . . ∩ A n ‾ ∣ = ∣ S ∣ − ∣ A 1 ∪ A 2 . . . ∪ A n ∣ = ∣ S ∣ − ∑ i ∣ A i ∣ + ∑ i , j ∣ A i ∩ A j ∣ . . . ( − 1 ) n ∣ A 1 ∩ A 2 . . . ∩ A n ∣ \begin{aligned} ans&=|\overline {A_1}\cap\overline {A_2}...\cap\overline {A_n}|\\ &=|S|-|A_1\cup A_2...\cup A_n|\\ &=|S|-\sum_{i}|A_i|+\sum_{i,j}|A_i\cap A_j|...(-1)^n|A_1\cap A_2...\cap A_n| \end{aligned} ans=A1A2...An=SA1A2...An=SiAi+i,jAiAj...(1)nA1A2...An
后面这个可以不用真的去枚举,也是可以用背包算。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值