Maximum Subsequence CodeForces - 888E (二分+状压)

本文介绍了一种通过状态压缩和二分查找解决模数最大化问题的方法。该问题要求选择一个整数序列,使得序列中元素之和对给定整数m取模的结果最大化。文章详细解释了如何将原始数据分为两部分进行处理,并使用状态压缩技巧来枚举所有可能的子集和,最终找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given an array a consisting of n integers, and additionally an integer m. You have to choose some sequence of indices b1, b2, …, bk (1 ≤ b1 < b2 < … < bk ≤ n) in such a way that the value of is maximized. Chosen sequence can be empty.

Print the maximum possible value of .

Input
The first line contains two integers n and m (1 ≤ n ≤ 35, 1 ≤ m ≤ 109).

The second line contains n integers a1, a2, …, an (1 ≤ ai ≤ 109).

Output
Print the maximum possible value of .

Example
Input
4 4
5 2 4 1
Output
3
Input
3 20
199 41 299
Output
19
Note
In the first example you can choose a sequence b = {1, 2}, so the sum is equal to 7 (and that’s 3 after taking it modulo 4).

In the second example you can choose a sequence b = {3}.

这题的数据范围是35,去枚举可能是不可能的,但是如果n为17或者18,也是可以的,所以这里用状压,把数据先分成两部分,然后把其中一部分用状压枚举所有可能(记得取模),然后排序去重,然后去枚举另一半的组合也是状压去枚举(v1),然后在左边去二分查找值(v2),应该这样去找,使得v1+v2最大,当然最大只能为m-1。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
int a[262148];
int p[40];
int findd(int limit,int num)//返回的是最接近num的值,但是不会大于num
{
    int l=0,r=limit-1;
    if(a[r]<=num)
        return a[r];
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[mid]==num)
            return a[mid];
        if(a[mid]>num)
            r=mid-1;
        else
            l=mid+1;
    }
    return a[l-1];
}
int main()
{
    int n,m;
    int k1=0;
    int k2=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",p+i),p[i]%=m;
    int limit=n/2;
    for(int i=0;i<(1<<limit);i++)
    {
        int t=0;
        for(int j=1;j<=limit;j++)
            if(i&(1<<(j-1)))
                t=(t+p[j])%m;
        a[k1++]=t;
    }
    sort(a,a+k1);
    int num=unique(a,a+k1)-a;

    int left=n-limit;
    int ans=0;
    for(int i=0;i<(1<<left);i++)//枚举
    {
        int temp=0;
        for(int j=1;j<=left;j++)
            if(i&(1<<(j-1)))
            temp=(temp+p[limit+j])%m;
        int now=m-1-temp;
        int get=findd(num,now);
        //cout<<temp<<" "<<get<<endl;
        ans=max(ans,get+temp);
        if(ans==m-1)//如果最大就没必要举继续找了
            break;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值