Codeforces Round #426 (Div. 2) D. The Bakery

題目鏈接
給一個序列,要求把序列劃分成k段,每一段裏的權值是這一段裏不同數字的個數。求如何劃分使得k個區間的權值和最大。
我們容易想到dp[i][j]代表前j個分爲i段時的最大值

    dp[i][j] = dp[i-1][k] + size(k+1 , j) ( 0<=k<j )
    //size(a,b)表示a到b這個區間裏有多少個不同數字

但是這顯然是個二維的方程,比賽時怎麼都想不到,賽後看題解看到了很巧妙的解決方法。
當枚舉到num[j]的時候,把num[j]這個數字最近一次出現的位置記錄一下p,然後把dp[i-1][p]到dp[i-1][j-1]加上1,然後dp[i][j] = max(dp[i-1][k]) ( i-1<=k < j)
其實這裏的dp[i][k]存的是當前枚舉到j時從k+1開始開始重啓一段能夠獲得的價值。[ 這個地方可以算是求不同顏色的一種方法(get到新知識) ],於是我們在查詢和加1的操作都可以通過線段樹來維護.

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
int dp[55][40000] ;
int N , K ;
int pos[40000] ;
int num[40000] ;
int tree[35000 * 4 + 2000] ;
int lazy[35000*4 +2000] ;
void Push_up(int rt)
{
    tree[rt] = max (tree[rt<<1] , tree[rt<<1|1]) ;
}
void Build(int l ,int r ,int rt , int x)
{
    lazy[rt] = 0 ;
    if(l == r)
    {

        tree[rt] = dp[x][l] ;
         return ;
    }
    int mid = (l + r) /2 ;
    Build(l , mid , rt<<1 , x) ;
    Build(mid + 1, r , rt<<1|1 , x) ;
    Push_up(rt) ;
}
void Push_down( int rt)
{
    if(lazy[rt])
    {
        tree[rt<<1] += lazy[rt]  ;
        tree[rt<<1|1] += lazy[rt] ;
        lazy[rt<<1] += lazy[rt] ;
        lazy[rt<<1|1] += lazy[rt] ;
        lazy[rt ] = 0 ;
    }
}
void Update(int L, int R , int  l ,int r , int rt )
{
    if(L <= l && r <= R)
    {
        tree[rt] ++ ;
        lazy[rt] ++ ;
        return ;
    }
    int mid = (l +r) / 2;
    Push_down(rt) ;
    if(L <= mid )
        Update( L ,R , l ,mid ,rt<<1 ) ;
    if( R > mid )
    Update(L ,R , mid + 1, r , rt<<1|1 ) ;
    Push_up(rt ) ;
}
int Que(int L ,int R , int l ,int r ,int rt)
{
    if(L <= l &&r <= R)
    {
        return tree[rt] ;
    }
    int mid = ( l + r) / 2;
    int res =0 ;
    Push_down(rt) ;
    if(L <= mid)
    res = max(res , Que(L , R , l , mid , rt << 1) ) ;
    if(R > mid )
    res = max(res , Que(L , R , mid + 1 , r , rt <<1|1 ) ) ;
    return res ;
}
int main()
{
    cin>> N >> K ;
    clr0(pos) ;
    clr0(dp) ;
//    num[1] = 0;
    for(int i = 1; i<= N ; ++ i)
        scanf("%d",&num[i]) ;
//        cout<<"FSDFGFD";
    for(int j = 1; j<= N ; ++j)
        if(!pos[num[j]])
        dp[1][j] =dp[1][j-1]+1 , pos[num[j]] = j ;
        else
        dp[1][j] = dp[1][j-1] ;

    for(int i = 2 ; i<= K ; ++ i)
    {
        clr0(pos ) ;
        Build(1,N,1,i-1) ;
        for(int j = i ; j<= N ; ++ j)
        {
            int t = pos[num[j]] ;
            if(!t) t = 1;
            Update(t , j-1 , 1, N ,1) ;
            int res = 0 ;
            res = Que(i-1 ,j-1 , 1 , N ,1);
            dp[i][j] = res ;
            pos[num[j]] = j ;
        }
    }
    cout<<dp[K][N]<<endl;

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章