2017 9 22 模拟赛T2

以下为不影响题意的简化版:

给出一个含有n个元素的集合a,要求在集合a中取所有含有k个元素的子集,定义sum的值为子集中最大值与最小值的差。求sum的值,答案对mod取模。

(这里的集合不同于数学上的集合,即使两个数数值相同也被视作不同的元素)

输入:

第一行三个整数 n,k,mod;

第二行n个整数,表示集合a

 

输出:

一个整数,sum,对mod取模。

 

样例输入:

4 2 10007

10 20 30 40

 

样例输出:

100

 

样例解释:

六个子集:(10,20)(10,30)(10,40)(20,30)(20,40)(30,40)

 

数据范围:

30% n<=20

60% n<=5000

100% 1<=k<=n<=100000,0<=a[i]<=1e9,2<=p<=1e9+7,保证p为质数。

 

心路历程:其实看到这个题10min内就想出了正解,但是缺少必要的优化知识以及对于可能出现的爆int情况的考虑于是写炸了。。。

算法分析:

假设所有的数据都是从小到大排列的,由于集合具有无序性,这并不会影响结果。

很显然,无论每个元素都是几,集合的选定方法都是确定的,每个集合都有一个最大值和一个最小值(这不是废话吗)。

于是我们可以将所有的最大值与最小值分开计算,从第k位到第n位,每个元素都可能成为最大值,那么其他k-1个元素如何选定?假设第x位上的数现在是最大值,那么前面x-1个元素中,必有k-1个元素将成为子集中的其他元素,也就是x-1个数选k-1个数,这是什么?组合数,于是所有最大值的和就求出来了。最小值同理。

以下为不影响思路的代码,为了看着舒服(顺便吐槽这题标程写的略辣鸡),删去了部分以long long为中间结果的计算过程(简单点说就是默认数据不会爆int),所以嘛,想要通过这个题,还要加点强制类型转换。

 

#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,a[100005],fac[100005],mod,ans;
int pw(int a,int b)
{
    int r=1;
    while(b>0)
    {
        if(b&1) r=r*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return r;
}
int c(int n,int m)
{
    return fac[n]*pw(fac[m]*fac[n-m]%mod,mod-2)%mod;
}
int main()
{
    scanf("%d%d%d",&n,&k,&mod);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    fac[0]=1;
    for (int i=1;i<=n;++i) fac[i]=fac[i-1]*i%mod;
    sort(a+1,a+n+1);
    for(int i=k;i<=n;++i) ans=(ans+a[i]*c(i-1,k-1)%mod)%mod;
    for(int i=1;i<=n-k+1;++i) ans=(ans-a[i]*c(n-i,k-1)%mod+mod)%mod;
    printf("%d",ans);
    return 0;
}

 

posted @ 2017-09-22 18:49  Excim  阅读(148)  评论(0编辑  收藏  举报