[hihocoder1546]集合计数

来源:互联网 发布:网络爬虫的目的和意义 编辑:程序博客网 时间:2024/06/15 13:07

题目描述

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个包含N个整数的集合S={A1, A2, … AN},以及一个给定的整数K,请计算有多少个S的子集满足其中的最大值与最小值的和小于等于K。

例如对于S={4, 2, 5, 8}以及K=7,满足的条件的子集有以下4个:{2}, {2, 4}, {2, 5}, {2, 4, 5}。

输入
第一行包含两个整数N和K。

第二行包含N个整数A1, A2, … AN。

对于30%的数据,1 <= N <= 20

对于70%的数据,1 <= N <= 1000

对于100%的数据,1 <= N <= 100000, 0 <= Ai <= 1000000000, 0 <= K <= 2000000000, A1 ~ AN 两两不同。

输出
输出满足条件的子集数目。由于答案可能非常大,你只需要输出答案除以1000000007的余数。

样例输入
4 7
4 2 5 8
样例输出
4

算法简介

先确认题面,笔者错读了几个地方,导致一直错解:1. 是最大值和最小值的和,这句话本来被笔者理解为了集合的和,修正之后又忘记了是两个的和(对于集合内个数为1的情况,也要加两次) 2. 对于和取模。然后开始解题:
最大值和最小值的和小于某个给定值的子集数目,首先肯定想到要排序。这里笔者直接采用了algorithm里面的sort函数,复杂度应该为O(NlogN)

对于排序之后的序列,最简单的方法就是逐一寻找最大值和最小值对,然后求取以两者为最大小值的子集个数:采用第n小的元素作为最小值,第m小的元素作为最大值,则以两者为最大小值的子集个数为2mn1(最大小值必定位于集合内,大小位于两者之间的元素有mn1个,每个元素都有两种可能性,出现/不出现),然后逐一比较任意两个元素,复杂度为C2N+O(N)=O(N2)。注意到计算量在于需要逐一比较任意两个元素,需要加以缩减:
设排序之后的列表为arr,从小到大排序
1. 若arr[i]+arr[j] <= K,则有arr[i]+arr[k] <= K, for k < j && k >= i
2. 若arr[i]*2 > K,则有arr[i] + arr[j] > K, for any j > i
3. 若arr[i]+arr[j+1] > K,则有arr[i+1]+arr[j+1] > K
利用第一条,对于任意最小值arr[i],只需要找到满足arr[i]+arr[j]<=K中的最大值即可,对于任意k < j && k >= i,arr[k]和arr[i]都可以作为最大小值对。而所有子集的个数为jk=i+12ki1+1=2ji
利用第二条,枚举arr[i]的时候,从小到大,一旦遇到2*arr[i] > K的情况,即可停止枚举。
利用第三条,最小值从小到大的枚举过程中,最大值的枚举也是从大到小,不需要每次都重新搜索arr[j],只需要从arr[i-1]的arr[j]继续向小枚举即可。
利用上诉三条,最终使得复杂度减为O(N)

对于2n的计算,因为需要多次使用,所以使用动态规划,缩减复杂度,最终最多需要计算N次计算即可,即复杂度为O(N)

代码

#include <iostream>#include <vector>#include <algorithm>using namespace std;#define MOD 1000000007int N,K;vector<int> arr(100002);int exp_2[100002];int exp2(int i) {    if (exp_2[i] == 0)        exp_2[i] = (2*exp2(i-1))%MOD;    return exp_2[i];}int main(){    exp_2[0] = 1;    cin >> N >> K;    for (int i = 0;i < N;++i)        cin >> arr[i];    sort(arr.begin(),arr.begin()+N);//    for (int i = 0;i < N;++i)//        cout << arr[i] << ' ';//    cout << endl;    int rear = N-1;    int result = 0;    for (int i = 0;i < N && 2 * arr[i] <= K;++i) {        result = (result + 1) % MOD;        for (;rear >= i && arr[i]+arr[rear] > K;--rear);        if (rear >= i)            result = (result+exp2(rear-i)-1)%MOD;    }    cout << result << endl;    return 0;}
原创粉丝点击