Solution -「CF 888E」Maximum Subsequence

cirnovsky /

§ 题意简述

见翻译

§ 题解

记录一下犯下的一个 nt 错误。

首先我们有一个显然的 DFS 暴力,每次两种决策,选或不选,所以时间复杂度为 Θ(2n)\Theta(2^{n})

nn 的范围是 35,是过不了的,我们可以考虑折半搜索。

关于折半搜索可以看看 我的折半搜索小计

暴力搜出 [1,n2],[n2+1,n][1,\lfloor\frac{n}{2}\rfloor],[\lfloor\frac{n}{2}\rfloor+1,n] 的所有答案,记录到两个 vector 里面。

这一部分的时间复杂度是 Θ(2n2)\Theta(2^{\lfloor\frac{n}{2}\rfloor})

考虑合并贡献。

先考虑一个暴力合并贡献的方法。

我们记第一次搜索搜出来的答案序列为 A1A_{1},同理有 A2A_{2}

这里的两个答案序列都是在模 mm 意义下的。

那么对于每一个 A1,iA_{1,i},我们都可以暴力在 A2A_{2} 中寻找两者相加模 mm 的最大值。

那么我们可以分类讨论了,因为序列在模 mm 意义下,所以我们对于每一个 A1,iA_{1,i} 找到的 A2,jA_{2,j} 使得 (A1,i+A2,j)modm(A_{1,i}+A_{2,j})\bmod m 最大,都只有两种情况。

一种是 A2,jA_{2,j}A2A_{2} 中值域范围在 [0,mA1,i1][0,m-A_{1,i}-1] 的所有值中最大,一种是在 A2A_{2} 中值域范围在 [0,m×2A1,i1][0,m\times2-A_{1,i}-1] 的所有值中最大。

所以我们在这两种情况中取最大即可。

由于我不理解二分做法,所以我用的是动态开点值域线段树。

(flag:动态开点不加引用就【】)

#include<bits/stdc++.h>
using namespace std;
const int N=35+5;
const int H=99999999;
int n,m,tot=1,root=1,ans,a[N];
struct Tree
{
	int ls,rs,val;
} nodes[(1<<(N>>1))<<3];
vector<int> vec[2];

void dfs(int x,int cur,int lim)
{
	if(x>lim)
	{
		if(lim==(n>>1)) 	vec[0].push_back(cur);
		else	vec[1].push_back(cur);
		return ;
	}
	dfs(x+1,(cur+a[x])%m,lim);
	dfs(x+1,cur,lim);
}

void ins(int &p,int l,int r,int x)
{
	if(p==0)	p=++tot;
	if(l==r)
	{
		nodes[p].val=l;
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=x)	ins(nodes[p].ls,l,mid,x);
	else	ins(nodes[p].rs,mid+1,r,x);
	nodes[p].val=max(nodes[nodes[p].ls].val,nodes[nodes[p].rs].val);
}

int find(int p,int l,int r,int x,int y)
{
	if(l>y||r<x)	return 0;
	if(l>=x&&r<=y)	return nodes[p].val;
	int mid=(l+r)>>1,ret=0;
	if(mid>=x)	ret=max(ret,find(nodes[p].ls,l,mid,x,y));
	if(mid<y)	ret=max(ret,find(nodes[p].rs,mid+1,r,x,y));
	return ret;
}

void output(int p)
{
	if(nodes[p].ls==0&&nodes[p].rs==0)
	{
		printf("%d ",nodes[p].val);
		return ;
	}
	output(nodes[p].ls);
	output(nodes[p].rs);
}

signed main()
{
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i)	scanf("%d",&a[i]);
	dfs(1,0,n>>1);
	dfs((n>>1)+1,0,n);
	sort(vec[0].begin(),vec[0].end());
	sort(vec[1].begin(),vec[1].end());
	for(auto x:vec[1])	ins(root,0,m-1,x);
	for(auto x:vec[0])	ans=max(ans,max(x+find(1,0,m-1,0,m-x-1),(x+find(1,0,m-1,0,m*2-x-1))%m));
	printf("%d\n",ans);
	return 0;
}