HihoCoder1546集合计数

这个题想了我超级久,最后还想错了,多亏有大佬指点才得以理清思路

首先是要求集合,并且题目也提示了没有相同元素,所以可以先排个序,这样我们就可以将这个排序后的数组当成是一个集合,然后去求满足

条件的小集合。所以排序可以帮我们获取最大值和最小值。

这时我们可以设两个变量表示数组的下标,一个是low,一个是high,因为获取小集合我们进行的操作就是从大集合中挑元素出去

由于N<=100000,所以N平方的做法是要超时的(我一开始就是超时),但是打表是完全木有问题的,所以我们将表示i个元素有x

种集合的表打出来,i表示元素个数,即数组下标,x代表这么多个元素可以形成的集合的个数。

为什么可以打这样一个表呢,因为只要一个集合的最大值和最小值满足条件,那么去掉这个最大值后的子集也是满足条件的,他们中的元素有包含和不包含两种情况

所以每次都是乘2

这样我们就得到了一个映射关系,但刚才那个逻辑推过来是要超时的,比如2 4 5 8,2和8中间有两个元素,所以可以产生4种集合,然而这样你就还必须

求2,4,5和4,5,8所能产生的集合数,这样写下来是要用两层循环的,所以会超时。(我就是在这里卡了好久)

但是后来我发现,2 4 5 8的那两个使得计算超时的子集都包含4 5两个元素,所以我要设法使得一次计算可以去掉两边的元素

这时我发现2 4 5 8的子集的数量=2 4 5+4 5 8+4 5(这个式子只是用于理解,正确性估计是没有的hhh)

所以我可以得到 2 4 5 8的集合数等于F[2 8 之间的元素个数+1]-1,有了这个式子,复杂度就只有O(N)了


代码

多想想,草稿纸上多写几个,就会理解了


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long num[100005];
long long   F[100005];
void f()
{
    F[0]=1;
    for(int i=1;i<100003;i++)
    {
        F[i]=F[i-1]*2;
        F[i]%=1000000007;
    }
}
int main()
{
    int N;
    f();
    long long K;
    while(~scanf("%d%lld",&N,&K))
    {
        long long ans=0;
        for(int i=1;i<=N;i++)
        {
            scanf("%lld",&num[i]);
            if(num[i]*2<=K)
                ans++;              //提前算掉这些一个点的集合个数,以后的集合就可以保证有两端了
        }
        sort(num+1,num+N+1);
        int low=1,high=N;
        while(low<=N)               //每次都要移动low和high,low
        {
            while(num[high]+num[low]>K&&high>=low)   //每次都滤掉那些数值过大(相对于现在的low来说)的元素
            {
                high--;
            }
            if(high>=low)       //然后就计算
            {
                ans+=F[high-low]-1;
                ans%=1000000007;
            }
            low++;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值