CF811C Solution

题目链接

题解

dp题呐,需要预处理出\(sum,lst,fst\)数组,\(sum[l][r]\)表示区间\([l,r]\)依题意的异或值,\(lst[i]\)表示\(a\)数组中值为\(i\)的元素最后一次出现的下标,\(fst[i]\)表示\(a\)数组中值为\(i\)的元素第一次出现的下标。

状态:\(dp[i]\)表示\([a_1,a_i]\)的异或最大和。

转移:找到每一个可以划为车厢的区间\([l,r]\)\(dp[r]=max(dp[r],dp[l-1]+sum[l][r])\)

至于寻找车厢区间,车厢区间需要满足其中所有元素\(lst[a_j]\le r\)\(fst[a_j]\ge l\)。由\(r\)\(1\)倒序遍历\(a\)数组,如果出现\(lst[a_j]>r\)则退出循环,如果当前全部\(fst[a_k]\ge j\)\(fst\)最小值\(\ge j\))则更新\(dp\)值。

目标状态:\(dp[n]\)

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=5010;
int a[N],dp[N],sum[N][N],lst[N],fst[N];
bool qwq[N];//qwq[i]:当前循环中值为i的元素是/否(1/0)出现过
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
	{
		memset(qwq,0,sizeof(qwq));
		sum[i][i]=a[i]; qwq[a[i]]=1;
		for(int j=i+1;j<=n;j++) 
		{
			if(!qwq[a[j]]) sum[i][j]=a[j]^sum[i][j-1];//第一次出现
			else sum[i][j]=sum[i][j-1];
			qwq[a[j]]=1;
		}
		lst[a[i]]=i;
		if(!fst[a[i]]) fst[a[i]]=i; 
	}
	for(int i=1;i<=n;i++)
	{
		int s=fst[a[i]];//s:当前循环中fst[j]最小值
		for(int j=i;j>=1;j--)
		{
			if(lst[a[j]]>i) break;
			s=min(s,fst[a[j]]);
			if(j==s) dp[i]=max(dp[i],dp[j-1]+sum[j][i]);//易得s>=j
		}
		dp[i]=max(dp[i],dp[i-1]);
	}
	printf("%d",dp[n]);
	return 0;
}
posted @ 2021-03-08 20:13  violet_holmes  阅读(56)  评论(0编辑  收藏  举报