CF834D

题目链接:http://codeforces.com/contest/834/problem/D

题目大意:将一个有n个数的数列分成k段,每段的价值为该段中不同数字的个数,求k段的最大总价值。

解题思路:

  思路来自叉姐 + GreenGrape

  dp + segment trees.

  dp不难想到。前 i 个数分成 j 段的最大价值:dp[i][j] = max( dp[i-1][k] + w(k+1,j), i-1 <= k < j). 但其实这样直接去搞的话分分钟TLE。

  所以,我们需要使用线段树。详情请看代码,里面有个人的注释,请指教。

AC代码:

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <iostream>
 4 #include <cmath>
 5 #include <cstring>
 6 #include <vector>
 7 #include <map>
 8 #include <set>
 9 #include <stack>
10 #include <string>
11 #include <queue>
12 
13 using namespace std;
14 #define lson l , m , rt << 1
15 #define rson m + 1 , r , rt << 1 | 1
16 #define root 1 , N , 1
17 const int maxn=35000+5;
18 int a[maxn],dp[52][maxn],last[maxn];
19 int pre[maxn];
20 int tree[maxn<<2],lazy[maxn<<2];
21 
22 //*****************************************************
23 //这一部分其实就是走模板
24 void pushup(int rt){
25     tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
26 }
27 void pushdown(int rt){
28     if(lazy[rt]){
29         lazy[rt<<1]+=lazy[rt];
30         lazy[rt<<1|1]+=lazy[rt];
31         tree[rt<<1]+=lazy[rt];
32         tree[rt<<1|1]+=lazy[rt];
33         lazy[rt]=0;
34     }
35 }
36 void update(int L,int R,int c,int l,int r,int rt){
37     if(L<=l&&r<=R){
38         lazy[rt]+=c;
39         tree[rt]+=c;
40         return;
41     }
42     pushdown(rt);
43     int m=(l+r)>>1;
44     if(L<=m)    update(L,R,c,lson);
45     if(m<R)     update(L,R,c,rson);
46     pushup(rt);
47 }
48 int query(int L,int R,int l,int r,int rt){
49     if(L<=l&&r<=R)
50         return tree[rt];
51     pushdown(rt);
52     int m=(l+r)>>1;
53     int ret=0;
54     if(L<=m)    ret=max(ret,query(L,R,lson));
55     if(m<R) ret=max(ret,query(L,R,rson));
56     return ret;
57 }
58 //*******************************************************
59 
60 
61 int main(){
62     int n,k;
63     scanf("%d%d",&n,&k);
64     for(int i=1;i<=n;i++){
65         scanf("%d",&a[i]);
66         pre[i]=last[a[i]];//pre[i]记录a[i]上一次出现的位置
67         last[a[i]]=i;
68     }
69     for(int i=1;i<=k;i++){
70         memset(tree,0,sizeof(tree));
71         memset(lazy,0,sizeof(lazy));
72         for(int j=i-1;j<=n;j++)
73             update(j,j,dp[i-1][j],0,n,1);//把线段树各叶子结点的值初始化为dp[i-1][]的值,在dp[i][]这一维度上的操作其实就是在dp[i-1][]的基础上进行的。前面不能忘了把线段树的数据置0。
74         for(int j=i;j<=n;j++){
75 //对于以x为结尾(pre[j] <= x <= j-1)的dp[i-1][x],将a[j]作为第 i 段的结尾可以使得dp[i-1][x]对应的dp[i][j]的值+1。
76 //故此时线段树维护的就是max(dp[i-1][k] + w(k+1,j), i-1 <= k < j)。w(k+1,j)在这个更新的过程中逐次加和累积。实在是精妙无比。
77             update(pre[j],j-1,1,0,n,1);
78             dp[i][j]=query(0,j,0,n,1);
79         }
80     }
81     int ans=0;
82     for(int j=k;j<=n;j++){
83         if(dp[k][j]>ans)    ans=dp[k][j];
84     }
85     printf("%d
",ans);
86     return 0;
87 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”
原文地址:https://www.cnblogs.com/Blogggggg/p/7352981.html