CF 888E Maximum Subsequence
一道比较套路的题,看到数据范围就差不多有想法了吧。
题目大意:给一个数列和m,在数列任选若干个数,使得他们的和对m取模后最大
取膜最大,好像不能DP/贪心/玄学乱搞啊。n\le35?果断meet in middle
考虑我们已经搜出了序列前一半的解,那么怎么根据后面的结果合并出结果?
设我们现在得到的和为x(对m取膜后),我们令一个数y=m-x,然后在前面的解中查找y的前驱即可
接下来进行简单的证明:
- 若可以找到前驱z,由于z<y,故x+z<m。又因为z=max(s\in[1,y-1]),故此时值最大。
- 若无法找到前驱z,此时我们取任何一个值s都会导致x+s>x+y=m,此时(x+s)\ mod\ m<x(这个很好理解吧)
于是我们每次都二分找出前缀,并取max即可。
CODE
#include<cstdio>
#include<cctype>
#include<algorithm>
using namespace std;
const int N=40;
int a[N],n,m,sum[1<<20],cnt,ans;
inline char tc(void)
{
static char fl[100000],*A=fl,*B=fl;
return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline int find(int x)
{
int l=1,r=cnt,res;
while (l<=r)
{
int mid=l+r>>1;
if (sum[mid]<x) res=sum[mid],l=mid+1; else r=mid-1;
}
return res;
}
inline void init(int now,int tot)
{
if (now>(n>>1)) { sum[++cnt]=tot; return; }
init(now+1,(tot+a[now])%m); init(now+1,tot);
}
inline void DFS(int now,int tot)
{
if (now>n) { ans=max(ans,tot+find(m-tot)); return; }
DFS(now+1,(tot+a[now])%m); DFS(now+1,tot);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
register int i; read(n); read(m);
for (i=1;i<=n;++i) read(a[i]);
init(1,0); sort(sum+1,sum+cnt+1); DFS((n>>1)+1,0);
return printf("%d",ans),0;
}
辣鸡老年选手AFO在即
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步