Codeforces 834D The Bakery (线段树+DP+单调性质)*

题目链接:https://cn.vjudge.net/problem/CodeForces-834D

#include<bits/stdc++.h>
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define root l,r,rt
#define ll long long
const int  maxn =1e5+5;
const int mod=1e9+7;
ll powmod(ll x,ll y) {ll t;for(t=1;y;y>>=1,x=x*x%mod) if(y&1) t=t*x%mod;return t;}
ll gcd(ll x,ll y)  {  return y==0?x:gcd(y,x%y); }
/*
题目大意:很像组合优化的题目类型了,
给一段数字序列,和k个划分限制,最多分成k块,
每一块的权重是块中数字的不同个数,问划分权重最大的是多少。

DP思想不难想到,随手就是一个暴力DP,O(n^2*k).
现在需要把一个n优化成一个log。
首先分析下区间性质,
假设DP滚动数组更新到j位置,
那么要使得最终j位置的答案能通过查询极值得到,
该更新哪个区间呢?注意,我们的目标是得到最终最大的答案,
就是说没必要真的每部分的答案都完美的更新到,
这个思想很重要。不难发现,DP数组是随横坐标递增的,
不同数字个数的函数也是递增的,
假设j位置的字符,上一个出现的位置是i,
那么很明显DP值在i到j-1的区间的所有值都要加一,
因为在这个区间所产生的所有新划分都有一个附加值,
下面也是我死活也搞不懂如何优化个log的情况,我以为
就算是用线段树在更新每个DP值时O(n^2)也难逃。。。。
其实有个很重要的性质,那就是在last[j]以前位置的划分,
不可能比更新过的值大了,因为新加一个数答案最多浮动1,
而前面的长的DP值已经更新完1了,短的DP值已经不能成为答案了。
所以只要更新一端区间就可以得到当前的DP值。
*/
int n,k;
int dp[maxn],a[maxn];
int last[maxn];
///线段树模板
int tree[maxn<<2],lazy[maxn<<2];///维护极值
void pushup(lrt){tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);}
void pushdown(lrt)
{
    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 build(lrt)
{
    lazy[rt]=0;
    if(l==r) {tree[rt]=dp[l];return ;}
    int mid=l+r>>1;
    build(lson),build(rson),pushup(root);
}
void update(lrt,int L,int R,int v)
{
    if(L<=l&&r<=R)
    {
        tree[rt]+=v;
        lazy[rt]+=v;
        return ;
    }
    pushdown(root);
    int mid=l+r>>1;
    if(L<=mid) update(lson,L,R,v);
    if(mid<R) update(rson,L,R,v);
    pushup(root);
}
int query(lrt,int L,int R)
{
    if(L<=l&&r<=R)  return tree[rt];
    int mid=l+r>>1,ans=0;
    pushdown(root);
    if(L<=mid) ans=max(ans,query(lson,L,R));
    if(mid<R) ans=max(ans,query(rson,L,R));
    pushup(root);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    for(int i=1;i<=k;i++)
    {
        build(0,n,1);
        for(int j=0;j<=n;j++) last[a[j]]=0,dp[j]=0;
        for(int j=1;j<=n;j++)
        {
            update(0,n,1,last[a[j]],j-1,1);///核心,不仅利用DP滚动数数组,还利用了答案的单调性来解决
            last[a[j]]=j;
            dp[j]=query(0,n,1,0,j-1);
        }
    }
    printf("%d\n",dp[n]);///滚动数组
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37451344/article/details/82526267