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

89 篇文章 1 订阅

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

题目传送门

一个经典问题

我们一个一个加入元素,第i个贡献的逆序对数量在区间 [ 0 , i − 1 ] [0,i-1] [0,i1]

问题也就是有多少个排列 x x x满足:
∑ i x i = k   ∣   x i ∈ [ 0 , i − 1 ] \sum _{i}x_i=k\ |\ x_i\in [0,i-1] ixi=k  xi[0,i1]
可以考虑容斥:

如果有j个不满足条件,也就是 x i ≥ i x_i\geq i xii

我们可以将不满足条件的 x i x_i xi都减去 i i i

然后剩下的插板法分配。

对于一对 i , j i,j i,j我们要知道有多少个排列 p p p满足:
KaTeX parse error: No such environment: align* at position 8: \begin{̲a̲l̲i̲g̲n̲*̲}̲ & (1). |p|=i\\…
我们可以设 d p i , j dp_{i,j} dpi,j表示满足条件 ( i , j ) (i,j) (i,j)的方案数。

有两种转移:

  • 整体+1, d p i , j ← d p i , j − i dp_{i,j}\leftarrow dp_{i,j-i} dpi,jdpi,ji
  • 整体+1,然后在开头添加一个1: d p i , j ← d p i − 1 , j − i dp_{i,j}\leftarrow dp_{i-1,j-i} dpi,jdpi1,ji

这样我们就可以满足前三个限制了,第四个限制怎么办呢?

如果我们在恰好有一个元素 > n >n >n的时候就将他减去,就会方便很多了。

则还有一个转移:

  • d p i , j ← − d p i − 1 , j − ( n + 1 ) dp_{i,j}\leftarrow -dp_{i-1,j-(n+1)} dpi,jdpi1,j(n+1)

可以发现 i ≤ n i\leq \sqrt{n} in ,所以时间复杂度为 O ( n × k ) O(\sqrt n\times k) O(n ×k)

/*
{
######################
#       Author       #
#        Gary        #
#        2020        #
######################
*/
#include <bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
//    int x=0;
//    char ch=getchar();
//    while(ch<'0'||ch>'9'){
//        ch=getchar();
//    }
//    while(ch>='0'&&ch<='9'){
//        x=(x<<1)+(x<<3)+(ch^48);
//        ch=getchar();
//    }
//    return x;
//}
const int INF = 0x3f3f3f3f;
typedef pair<int, int> mp;
/*}
*/
const int MOD = 1e9 + 7;
int n, k;
const int SQRT = 450;
const int MAXN = 1e5 + 20;
int dp[SQRT + 1][MAXN + 1]; //取了i个,和为j
int fact[MAXN + MAXN];
LL quick(LL A, LL B) {
    if (B == 0)
        return 1;

    LL  tmp = quick(A, B >> 1);
    tmp *= tmp;
    tmp %= MOD;

    if (B & 1)
        tmp *= A, tmp %= MOD;

    return tmp;
}
int inv(int x) {
    return quick(x, MOD - 2);
}
void add(int &A, int B) {
    A += B;

    if (A >= MOD)
        A -= MOD;
}
int c(int A, int B) {
    return 1ll * fact[A] * inv(fact[B]) % MOD * inv(fact[A - B]) % MOD;
}
int main() {
    scanf("%d%d", &n, &k);
    dp[0][0] = 1;
    rb(i, 1, SQRT) {
        rb(j, i, k) {
            dp[i][j] = (dp[i][j - i] + dp[i - 1][j - i]) % MOD;

            if (j > n) {
                add(dp[i][j], MOD - dp[i - 1][j - (n + 1)]);
            }
        }
    }
    fact[0] = 1;
    rb(i, 1, 200000) {
        fact[i] = 1ll * fact[i - 1] * i % MOD;
    }
    int rest = 0;
    rb(i, 0, k) {
        int tmp = c(k - i + n - 1, n - 1);
        rb(j, 0, SQRT) {
            int have = 1ll * tmp * dp[j][i] % MOD;

            if (j & 1) {
                have = MOD - have;
            }

            add(rest, have);
        }
    }

    cout << rest << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值