前后缀+组合规律+线性求阶乘逆元

Posted nonames

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前后缀+组合规律+线性求阶乘逆元相关的知识,希望对你有一定的参考价值。

https://atcoder.jp/contests/abc151/tasks/abc151_e

题意:给你n个数,从中任意选出k个数作为一组,求出任意组合的最大值-最小值之和。

解法:排序,前缀和和后缀和,组合规律。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<algorithm>
#include<iostream>
#include<map>
#define inf 0x3f3f3f3f
#define ll long long
#define maxx 5000000
#define mod 1000000007
using namespace std;
const int N = 100002;
ll a[N] , sum1[N] , sum2[N] , fac[N] , inv[N];

ll quickpow(ll a,  ll b)
{
    ll ans = 1 ;
    while(b)
    {
        if(b&1)
        {
            ans = ans * a % mod ;
        }
        b >>= 1 ;
        a = a * a % mod ;
    }
    return ans%mod;
}

void init()
{
    fac[0] = fac[1] = inv[0] = inv[1] = 1 ;
    for(int i = 2 ; i <= N-2 ; i++) fac[i] = fac[i-1] * i % mod;
    inv[N-2] = quickpow(fac[N-2] , mod-2);
    for(int i = N -3 ; i >= 2 ; i--) inv[i] = inv[i+1] * (i+1) % mod;
}

ll C(int n , int m)
{
    return fac[n]%mod * inv[m]%mod * inv[n-m]%mod;
}

int main()
{
    init();
    ll n , k ;
    scanf("%lld%lld" , &n , &k);
    for(int i = 1 ; i <= n ; i++)
    {
        scanf("%lld" , &a[i]);
    }
    sort(a+1 , a+n+1);
    for(int i = 1 ; i <= n ; i++)
    {
        sum1[i] = sum1[i-1] + a[i];
    }
    for(int i = n ; i >= 1 ; i--)
    {
        sum2[i] = sum2[i+1] + a[i];
    }
    ll a = k - 2 , b = a , ans = 0;
    for(int i = n - k + 1 , j = k ; i >= 1 ; i-- , j++ , a++)
    {
        ans = (ans%mod + (sum2[j] - sum1[i])%mod * C(a , b))%mod;
    }
    cout << ans << endl ;


    return 0 ;
}

以上是关于前后缀+组合规律+线性求阶乘逆元的主要内容,如果未能解决你的问题,请参考以下文章

数论干货线性方法求阶乘,逆元和组合数

线性求逆元 组合数

求组合数小结

组合数与除法逆元,阶乘逆元递推

线性递推阶乘的逆元

组合数+逆元