CF888E Maximum Subsequence

洛咕

题意:给定\(n(n<=35)\)个数的数列和\(m(m<=1e9)\),在数列中任选若干个数,使得他们的和对\(m\)取模后最大,求这个最大值.

分析:看到这个\(35\),很容易想到折半搜索.把数列分成相等的两个部分,分别爆搜出每个部分所有能够产生的和(在模\(m\)的意义下),时间复杂度为\(2^{18}\),很稳.

然后对于前半部分的一个数\(x\),它要么是跟后半部分最大的那个与之相加不超过\(m\)的数\(y\)产生最大答案,要么是跟后半部分最大的那个数\(q2[sum2]\)产生最大答案.

所以我们只要考虑如何求第一种情况,显然可以把两个部分的数从小到大排序,然后两个指针来尺取就好了.

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define ll long long
using namespace std;
inline int read(){
    int x=0,o=1;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')o=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*o;
}
const int N=600005;
int n,m,tot1,tot2,a[40],q1[N],q2[N];
inline void dfs1(int now,ll sum){
    q1[++tot1]=sum;if(now>n/2)return;
    dfs1(now+1,(sum+a[now])%m);dfs1(now+1,sum);
}
inline void dfs2(int now,ll sum){
    q2[++tot2]=sum;if(now>n)return;
    dfs2(now+1,(sum+a[now])%m);dfs2(now+1,sum);
}
int main(){
    n=read();m=read();for(int i=1;i<=n;++i)a[i]=read()%m;//一开始就可以取模
    dfs1(1,0);dfs2(n/2+1,0);//折半搜索1~n/2和n/2+1~n
    sort(q1+1,q1+tot1+1);sort(q2+1,q2+tot2+1);//两部分从小到大排序
    int sum1=unique(q1+1,q1+tot1+1)-q1-1;
    int sum2=unique(q2+1,q2+tot2+1)-q2-1;//去重
    int ans=max(q1[sum1],q2[sum2]);//可能的最大答案
    if(ans==m-1){printf("%d\n",ans);return 0;}//如果已经是最优解了就直接输出
    int l=0,r=sum2,L=sum2;//q1[1]=0,不用考虑了.从q1[2]开始考虑
    while(l<=r){
        int mid=(l+r)>>1;
        if(q2[mid]+q1[2]>=m){
            L=mid;r=mid-1;
        }
        else l=mid+1;
    }
    ans=max(ans,max(q1[2]+q2[L-1],(q1[2]+q2[sum2])%m));//q1[2]可能产生的最大答案
    for(int i=3;i<=sum1;++i){
        while(L>=2&&q2[L]+q1[i]>=m)--L;
        ans=max(ans,max(q1[i]+q2[L],(q1[i]+q2[sum2])%m));
    }
    printf("%d\n",ans);
    return 0;
}
上一篇:[CF1082D]Maximum Diameter Graph


下一篇:LeetCode T53.Maximum Subarray/最大子序和