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;
}
posted @ 2022-04-13 21:41  Plozia  阅读(34)  评论(0编辑  收藏  举报