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;
}