http://codeforces.com/contest/811/problem/C

【题意】

给定一个自然数序列,在这个序列中找出几个不相交段,使得每个段的异或值之和相加最大。

段的异或值这样定义:段中每个不同数字(不重复)相异或。

段有这样的要求:段中任意一个数字不会在段外出现。

【思路】

首先预处理每个数字第一次出现和最后一次出现的位置,这样对于一个区间[l,r]就很容易判断是否为满足题意的段。

然后区间DP,dp[i]表示子序列[1,i]的最大值。

状态转移:对于dp[i],最小值为dp[i-1],即a[i]在任意段外;如果a[i]的最后一次出现位置大于i,那么没有更新的余地;否则,可能找到这样的l,S.T.dp[i]=max(dp[i],dp[l-1]+sum[l,i])。

如何找到这样的l?

首先可以肯定的是l最小为first[a[i]],然后遍历[first[a[i]],last[a[i]]]中的每个数,不断更新l=min(l,first[a[k]])。

那么为什么dp[i]不可能是dp[j]+sum[j-1,i](j<l)?

因为这样的j一定不满足[j,i]是一个满足题意的段.

如果last[a[j]]<last[a[i]],那么刚刚在遍历[first[a[i]],last[a[i]]]的时候应该已经找到j,即j>=l,矛盾

如果last[a[j]]>last[a[i]],这样的段也不满足题意。

【TLE】

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <cmath>
 4 #include <vector>
 5 #include <algorithm>
 6 #include <set>
 7 #include <map>
 8 #include <queue>
 9 #include <deque>
10 #include <stack>
11 #include <string>
12 #include <bitset>
13 #include <ctime>
14 #include<algorithm>
15 #include<cstring>
16 using namespace std;
17 int n;
18 const int maxn=5002;
19 int a[maxn];
20 int fir[maxn];
21 int last[maxn];
22 int dp[maxn];
23 
24 int check(int l,int r)
25 {
26     int vis[maxn];
27     memset(vis,0,sizeof(vis));
28     for(int i=l;i<=r;i++)
29     {
30         if(last[a[i]]>r||fir[a[i]]<l)
31         {
32             return -1;
33         }
34     }
35     int x=0;
36     for(int i=l;i<=r;i++)
37     {
38         if(!vis[a[i]])
39         {
40             x^=a[i];
41             vis[a[i]]=1;
42         }
43     }
44     return x;
45 }
46 int main()
47 {
48     while(~scanf("%d",&n))
49     {
50         memset(dp,0,sizeof(dp));
51         memset(fir,0,sizeof(fir));
52         memset(last,0,sizeof(last));
53         for(int i=1;i<=n;i++)
54         {
55             scanf("%d",&a[i]);
56             if(!fir[a[i]])
57             {
58                 fir[a[i]]=i;
59              } 
60              last[a[i]]=i;
61         }
62         for(int i=1;i<=n;i++)
63         {
64         //    cout<<fir[a[i]]<<" "<<last[a[i]]<<endl; 
65         }
66         for(int i=1;i<=n;i++)
67         {
68             dp[i]=dp[i-1];
69             for(int k=1;k<=i;k++)
70             {
71                 int num=check(k,i);
72                 if(num>0)
73                 {
74                     dp[i]=max(dp[i],dp[k-1]+num);
75                 }
76             }
77         //    printf("dp[%d]=%d\n",i,dp[i]);
78         }
79         cout<<dp[n]<<endl;
80         
81     }
82     return 0;
83 }
View Code

相关文章: