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


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

    题目传送门

    一个经典问题

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

    问题也就是有多少个排列(x)满足:

    [sum _{i}x_i=k | x_iin [0,i-1] ]

    可以考虑容斥:

    如果有j个不满足条件,也就是(x_igeq i)

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

    然后剩下的插板法分配。

    对于一对(i,j)我们要知道有多少个排列(p)满足:

    [egin{align*} & (1). |p|=i\ & (2). sum p_i=j\ & (3). forall kin[2,i],p_{k}>p_{k-1}\ & (4). forall k,p_{k}in [1,n] end {align*} ]

    我们可以设(dp_{i,j})表示满足条件((i,j))的方案数。

    有两种转移:

    • 整体+1,(dp_{i,j}leftarrow dp_{i,j-i})
    • 整体+1,然后在开头添加一个1:(dp_{i,j}leftarrow dp_{i-1,j-i})

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

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

    则还有一个转移:

    • (dp_{i,j}leftarrow -dp_{i-1,j-(n+1)})

    可以发现(ileq sqrt{n}),所以时间复杂度为(O(sqrt n imes 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;
    }
    
  • 相关阅读:
    8.图片切换
    6.节点的访问关系和操作
    4.京东狗
    3.关闭京东广告栏
    1.事件的拓展
    小飞鸟 小游戏
    网页特效(旋转木马轮播图)
    时间转换
    深入理解css3中的flex-grow、flex-shrink、flex-basis
    VUE 使用插件vue-clipboard2复制内容至剪切板(两种使用方法)
  • 原文地址:https://www.cnblogs.com/gary-2005/p/14208320.html
Copyright © 2020-2023  润新知