codeforces D.The Bakery

本文解析了CodeForces D题(The Bakery),介绍了如何通过动态规划和区间修改线段树来解决该问题。通过维护子问题的最大价值并利用线段树进行高效的区间更新和查询,实现了O(nk*log^2n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

codeforces D.The Bakery

原题连接:
http://codeforces.com/contest/834/problem/D

对于前n个元素,分成k段,最大价值为 dp[n][k]
记,区间 [i,j] 的价值为 V[i][j]
则:

dp[n][k]=maxi=0n1(dp[i][k1]+V[i+1][n])

对于 dp[n+1][k] 我们有:

dp[n+1][k]=maxi=0n(dp[i][k1]+V[i+1][n+1])

影响 V[i+1][n+1] 的因素与数列中,上一次出现 an+1 的位置有关

_

记:上一次出现 aj 的位置为 t

那么就有:

V[i][j]=V[i][j1]+1,t<=i<j

V[i][j]=V[i][j1],0<i<t

使用区间修改线段树维护,便可缩短状态转移耗费时间。
总时间复杂度 O(nklog2n)
#include <algorithm>
#include <string.h>
#include <stdio.h>
#define MAXN 36000
using namespace std;

struct point
{
    int c[2];
    int m;//最大值
    int flag;

    point()
    {
        m=0;
        c[0]=c[1]=0;
        flag=0;
    }
};

struct Bt
{
    point A[MAXN*2];
    int ma;
    int deep;
    int root;
    int size;

    void clear()
    {
        for(int i=0;i<deep;i++)
        {
            A[i].m=0;
            A[i].flag=0;
            A[i].c[0]=A[i].c[1]=0;
        }
        deep=2;
    }

    Bt ()
    {

        deep=2;
        root=1;

    }

    void update(point &a)
    {
        if(A[a.c[0]].m > A[a.c[1]].m)
            a.m=A[a.c[0]].m;
        else
            a.m=A[a.c[1]].m;
        a.m+=a.flag;

    }

    void _add(int l,int r,int L,int R,int key,int k)
    {

        if(R<l||L>r)return;
        if(l<=L&&R<=r)
        {
            if(L<R)
            {
                A[k].flag+=key;
                A[k].m+=key;
            }
            else
            {
                A[k].m+=key;
            }
            return ;
        }
        int mid=(L+R)>>1;

        if(A[k].flag>0)
        {
            _add(L , R , L     , mid , A[k].flag , A[k].c[0]);
            _add(L , R , mid+1 , R   , A[k].flag , A[k].c[1]);
            A[k].flag=0;
        }

        _add(l , r , L     , mid , key       , A[k].c[0]);
        _add(l , r , mid+1 , R   , key       , A[k].c[1]);

        update(A[k]);
    }
    void add(int l,int r,int key)
    {
        _add(l,r,0,size,key,root);
    }
    void _insert(int x,int L,int R,int key,int &k)
    {
        if(x<L||x>R)return;
        if(!k)k=deep++;
        if(L==R)
        {
            A[k].m=key;
            return ;
        }
        int mid=(L+R)>>1;
        _insert(x,L,mid,key,A[k].c[0]);
        _insert(x,mid+1,R,key,A[k].c[1]);
        update(A[k]);
    }
    void insert(int x,int key)
    {
        _insert(x,0,size,key,root);
    }
    void _query(int l,int r,int L,int R,int k,int key)
    {
        if(R<l||L>r)return ;
        if(l<=L&&R<=r)
        {
            if(ma<A[k].m+key)
            {
                ma=A[k].m+key;
            }
            return ;
        }
        int mid=(L+R)>>1;
        key+=A[k].flag;
        _query(l , r , L , mid,A[k].c[0],key);
        _query(l , r , mid+1 , R,A[k].c[1],key);
    }

    void query(int l,int r)
    {
        ma=0;
        _query(l,r,0,size,root,0);
    }

}B;

int A[MAXN];
int L[MAXN];
int dp[MAXN];
int C[MAXN];

int main ()
{
    int n,k;
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",A+i);
        L[i]=C[A[i]];
        C[A[i]]=i;
    }
    B.size=n;
    for(int i=0;i<=n;i++) B.insert(i,0);
    for(int i=0;i<k;i++)
    {

        for(int j=1;j<=n;j++)
        {
            B.add(L[j] , j-1 ,1);
            B.query(0,j-1);
            dp[j]=B.ma;
        }

        B.clear();

        for(int j=0;j<=n;j++)    B.insert(j,dp[j]);

    }
    printf("%d\n",dp[n]);

    return 0;
}
### Codeforces Div.2 比赛难度介绍 Codeforces Div.2 比赛主要面向的是具有基础编程技能到中级水平的选手。这类比赛通常吸引了大量来自全球不同背景的参赛者,包括大学生、高中生以及一些专业人士。 #### 参加资格 为了参加 Div.2 比赛,选手的评级应不超过 2099 分[^1]。这意味着该级别的竞赛适合那些已经掌握了一定算法知识并能熟练运用至少一种编程语言的人群参与挑战。 #### 题目设置 每场 Div.2 比赛一般会提供五至七道题目,在某些特殊情况下可能会更多或更少。这些题目按照预计解决难度递增排列: - **简单题(A, B 类型)**: 主要测试基本的数据结构操作和常见算法的应用能力;例如数组处理、字符串匹配等。 - **中等偏难题(C, D 类型)**: 开始涉及较为复杂的逻辑推理能力和特定领域内的高级技巧;比如图论中的最短路径计算或是动态规划入门应用实例。 - **高难度题(E及以上类型)**: 对于这些问题,则更加侧重考察深入理解复杂概念的能力,并能够灵活组合多种方法来解决问题;这往往需要较强的创造力与丰富的实践经验支持。 对于新手来说,建议先专注于理解和练习前几类较容易的问题,随着经验积累和技术提升再逐步尝试更高层次的任务。 ```cpp // 示例代码展示如何判断一个数是否为偶数 #include <iostream> using namespace std; bool is_even(int num){ return num % 2 == 0; } int main(){ int number = 4; // 测试数据 if(is_even(number)){ cout << "The given number is even."; }else{ cout << "The given number is odd."; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值