早期作品,不喜轻喷。
序列分治板子题。 切这道题用了好长时间,所以想发篇题解作为纪念 。 首先,我们认真观察题目数据(面向数据做题是个好习惯),发现题目的\(n\)竟然只有\(35\),我们顿时感到打暴力的机会来了:\(2^n\)枚举? 是个好办法。 只可惜我们发现\(2^{35}=34359738368\),并不能过掉所有数据点,于是考虑优化。分治
考虑把这\(n\)个数分成两组(当然要尽量平均),对两组数据分别实施暴力,并把结果存起来(事实上是可以存下来的:\(2^{18}=262144\))。
void dfs1(int i,int sum){ if(i==b){p[++k]=sum,p[++k]=(sum+a[b])%m; return ;} dfs1(i+1,sum),dfs1(i+1,(sum+a[i])%m);}void dfs2(int i,int sum){ if(i==n){q[++t]=sum,q[++t]=(sum+a[n])%m; return ;} dfs2(i+1,sum),dfs2(i+1,(sum+a[i])%m);}
这样一来,我们就得到了原序列分成两半的结果,这两个序列中的数两两组合就可以得到我们要的结果。
等等,两两组合?这样的复杂度不是和纯暴力一样吗? 这时候就需要我们贪心地看问题了: 我们发现:对于序列\(p\)中的每一个数\(p_i\),在序列\(q\)中若能找到一个与之相加小于\(m\)的最大的数\(q_j\),其他所有的与\(p_i\)的和小于\(m\)的数都不会比它更优,即\(q_j\)比序列\(q\)中所有比它小的数都更优。 对于\(q\)中的每一个数,满足相同条件的\(p_i\)也具有同样的性质。 我们想到一种对于\(p,q\)线性的算法:把\(p\)和\(q\)排一遍序,把指向\(p\)数组的指针\(i\)和指向\(q\)数组的指针\(j\)分别按上面所说的条件向右和向左移动,同时更新\(ans\)。 这时我们就只剩下\(p_i+q_j>m\)的情况了,由于在之前已经取过模,\(p_i+q_j\)必定小于\(2m\),所以我们就只需要用\(p,q\)的最大值之和去更新一下\(ans\)就好了。代码实现
int main(){ R int i,j,ans=0; n=read(),m=read(),b=n>>1; for(i=1;i<=n;++i) a[i]=read(); if(n==1) printf("%d",a[1]%m),exit(0); dfs1(1,0),dfs2(b+1,0),i=0,j=t; sort(p+1,p+k+1),sort(q+1,q+t+1); while(i<=k){ while(p[i]+q[j]>=m) --j; ans=max(ans,p[i]+q[j]),++i; } ans=max(ans,p[k]+q[t]-m); printf("%d",ans); return 0;}
注意这里特判了一下\(n=1\)的情况,我被这个点坑了一次。