CF888E Maximum Subsequence 题解
首先看完本题,最直接的想法就是——爆搜!
但是, \(2^{35}\) 让我们望而却步,因此我们需要考虑一定的优化。
而本题的优化是十分经典的 折半搜索 (Meet in middle) 算法。
折半搜索的主要思路就是:将序列裂成两半搜索,然后合并答案。
对于这道题,我们首先对 \([1,\dfrac{n}{2}]\) 做一次暴力搜索, \([\dfrac{n}{2}+1,n]\) 做一次暴力搜索,统计的和存在 \(ans1,ans2\) 里面,其中不要忘记取模。
但是这样还是没有用啊?此时就要请出另一利器:尺取法。
首先对 \(ans1,ans2\) 排个序,然后指定 \(pos1=1,pos2=size(ans2)\) 。
由于此时 \(ans1,ans2\) 中每个数都在 \([0,m)\) 范围内。因此我们只需要考虑 \(ans1_{pos1}+ans2_{pos2} < m\) 的结果即可。为什么不需要考虑大于 \(m\) 的?因为最大值 \(ans1_{size(ans1)}+ans2_{size(ans2)} < 2n\) ,此时对于大于 \(m\) 的和而言,\(ans1_{size(ans1)}+ans2_{size(ans2)}\) 一定是最优解,因此不需要考虑。
想清楚后代码就很简单了。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 40 + 10;
int n, m, a[MAXN], ans1[1 << 20], ans2[1 << 20], mid, cnt1, cnt2, ans;
int read()
{
int sum = 0, fh = 1; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') fh = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {sum = (sum << 3) + (sum << 1) + (ch ^ 48); ch = getchar();}
return sum * fh;
}
void dfs1(int k, int sum)
{
if(k == mid + 1) {ans1[++cnt1] = sum; return ;}
dfs1(k + 1, sum); dfs1(k + 1, (sum + a[k]) % m);
}
void dfs2(int k, int sum)
{
if(k == n + 1) {ans2[++cnt2] = sum; return ;}
dfs2(k + 1, sum); dfs2(k + 1, (sum + a[k]) % m);
}
int main()
{
n = read(); m = read();mid = n >> 1;
for(int i = 1; i <= n; ++i) a[i] = read();
dfs1(1, 0); dfs2(mid + 1, 0);
sort(ans1 + 1, ans1 + cnt1 + 1); sort(ans2 + 1, ans2 + cnt2 + 1);
int pos1 = 1, pos2 = cnt2;
while(pos1 <= cnt1 && pos2 >= 1)
{
while(ans1[pos1] + ans2[pos2] >= m) pos2--;
ans = max(ans, ans1[pos1] + ans2[pos2]); pos1++;
}
printf("%d\n", max(ans, (ans1[cnt1] + ans2[cnt2]) % m));
return 0;
}