1. 程式人生 > 實用技巧 >題解 CF888E 【Maximum Subsequence】

題解 CF888E 【Maximum Subsequence】

題意簡述

見翻譯

題解

記錄一下犯下的一個 nt 錯誤。

首先我們有一個顯然的 DFS 暴力,每次兩種決策,選或不選,所以時間複雜度為 \(\Theta(2^{n})\)

\(n\) 的範圍是 35,是過不了的,我們可以考慮折半搜尋。

關於折半搜尋可以看看 我的折半搜尋小計

暴力搜出 \([1,\lfloor\frac{n}{2}\rfloor],[\lfloor\frac{n}{2}\rfloor+1,n]\) 的所有答案,記錄到兩個 vector 裡面。

這一部分的時間複雜度是 \(\Theta(2^{\lfloor\frac{n}{2}\rfloor})\)

考慮合併貢獻。

先考慮一個暴力合併貢獻的方法。

我們記第一次搜尋搜出來的答案序列為 \(A_{1}\),同理有 \(A_{2}\)

這裡的兩個答案序列都是在模 \(m\) 意義下的。

那麼對於每一個 \(A_{1,i}\),我們都可以暴力在 \(A_{2}\) 中尋找兩者相加模 \(m\) 的最大值。

那麼我們可以分類討論了,因為序列在模 \(m\) 意義下,所以我們對於每一個 \(A_{1,i}\) 找到的 \(A_{2,j}\) 使得 \((A_{1,i}+A_{2,j})\bmod m\) 最大,都只有兩種情況。

一種是 \(A_{2,j}\)\(A_{2}\) 中值域範圍在 \([0,m-A_{1,i}-1]\) 的所有值中最大,一種是在 \(A_{2}\)

中值域範圍在 \([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;
}