http://codeforces.com/problemset/problem/811/C
题意:
给一个长度为n的序列,你从中选取若干个区间(不能重叠),并且每个区间内的数只能出现在这个区间里 如 1 2 2 1 你可以选【2,3】,[1,4],不可以选 [1,2],[1,3],[2,4]; 每个区间的权为区间内去重后的异或和,之后区间加和,求最大。
思路:用DP求解,预处理出每个数第一次出现的位置和最后一次出现的位置。之后处理处[i]~[j]的区间异或和,之后我们维护下每个可能的区间和最大即可。
#include <bits/stdc++.h>
#define maxs 2020202
#define ll long long
#define inf LONG_LONG_MAX
#define mme(i,j) memset(i,j,sizeof(i))
using namespace std;
ll dp[5005],a[maxs];
ll n;
int st[maxs],ed[maxs];
bool vis[5005];
ll val[5005][5005];
int main()
{
while(~scanf("%lld",&n))
{
for(int i=1; i<=n; i++)
scanf("%lld",&a[i]);
mme(dp,0);
mme(st,0);
mme(ed,0);
mme(val,0);
for(int i=1; i<=n; i++)
{
if(st[a[i]])
st[a[i]] = min(i,st[a[i]]);
else
st[a[i]]=i;
if(ed[ a[i] ])
ed[ a[i] ] = max(i,ed[ a[i] ]);
else
ed[ a[i] ] =i;
}
for(int i=1; i<=n; i++)
{
mme(vis,0);
val[i][i]=a[i];
vis[ a[i] ] =1;
for(int j=i+1; j<=n; j++)
{
if(!vis[ a[j] ])
{
val[i][j] =val[i][j-1]^a[j];
vis[ a[j] ] =1;
}
else
val[i][j] = val[i][j-1];
}
}
int tp,ok=1;
for(int i=1; i<=n; i++)
{
if(i == ed[a[i]])
{
tp = a[i];
ok=1;
int left = st[tp];
for(int j = st[tp]+1; j<ed[tp]; j++)
{
if(ed[ a[j] ] >i)
{
ok=0;
break;
}
left = min(left,st[ a[j] ]);
}
if(ok)
dp[i]=max(dp[i-1],dp[left-1]+val[left][i]);
else
dp[i] = dp[i-1];
}
else dp[i]=dp[i-1];
}
printf("%lld\n",dp[n]);
}
return 0;
}