首页 > 其他分享 >P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

P8776 [蓝桥杯 2022 省 A] 最长不下降子序列

时间:2024-04-03 10:45:17浏览次数:32  
标签:down en int res pos len 蓝桥 2022 P8776

1.首先想到的做法

设up_len[i]为以a[i]为结尾的最长不下降子序列的长度,down_len[i]表示以a[i]为开始的最长不下降子序列的长度。

在求pre的过程中记录下额外信息:down_pre[i]表示在求down_len[i]的过程中,i是由哪个点转移过来的;

得到dp的转移方程:

if(down_pre[i])
            ans = max(ans, up_len[i] + down_len[down_pre[i]] + min(k, down_pre[i] - i - 1));
        else
            ans = max(ans, up_len[i] + min(k, n - i));

很好理解,这里采用了贪心

2.权值树状数组/权值线段树做法

参见这篇题解:

P8776 [蓝桥杯 2022 省 A] 最长不下降子序列 - 洛谷专栏 (luogu.com.cn)

 

!!!注意:

在最长不下降子序列中,

for (int i = 2; i <= n; i++)
    {
        if (a[i] >= en[res])
        {
            en[++res] = a[i];
            up_len[i] = res;
        }
        else
        {
            int pos = upper_bound(en + 1, en + res + 1, a[i]) - en;
            en[pos] = a[i];
            up_len[i] = pos;
        }
    }

这里应该是upper_bound而不是lower_bound,因为最长不下降子序列的en数组可能会有连续若干个值是相同的,如果采用lower_bound的话,那么修改其中一个,其他相等的也要同时做出修改;而upper_bound因为找到的值是唯一的,只需要单独修改这个点的值即可

完整代码:

1.二分dp做法:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <string.h>
#include <cmath>
#define R(x) x = read()
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;

const int N = 1e5 + 5;

int n, k;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int a[N], down_pre[N], up_len[N], down_len[N], en[N], id[N]; // id是反着做的辅助数组

int dp()
{
    // 正着做
    int res = 1, ans = -1;
    en[1] = a[1];
    up_len[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (a[i] >= en[res])
        {
            en[++res] = a[i];
            up_len[i] = res;
        }
        else
        {
            int pos = upper_bound(en + 1, en + res + 1, a[i]) - en;
            en[pos] = a[i];
            up_len[i] = pos;
        }
    }
    // for(int i = 1; i <= n; i++)
    //     cout << up_len[i] << " ";
    // 反着做
    res = 1;
    memset(en, 0, sizeof en);
    down_len[n] = 1;
    en[res] = -a[n];
    id[res] = n;
    for (int i = n - 1; i; i--)
    {
        int tmp = -a[i];
        if (tmp >= en[res])
        {
            down_pre[i] = id[res];
            en[++res] = tmp;
            id[res] = i;
            down_len[i] = res;
        }
        else
        {
            int pos = upper_bound(en + 1, en + res + 1, tmp) - en;
            en[pos] = tmp;
            id[pos] = i;
            down_len[i] = pos;
            down_pre[i] = id[pos - 1];
        }
        if(down_pre[i])
            ans = max(ans, up_len[i] + down_len[down_pre[i]] + min(k, down_pre[i] - i - 1));
        else
            ans = max(ans, up_len[i] + min(k, n - i));
    }
    /*for (int i = 1; i <= n; i++)
        cout << down_len[i] << " " << down_pre[i] << endl;*/
    ans = max(ans, up_len[n]);
    if(k >= n - 1)
        ans = n;
    return ans;
}

int main()
{
    R(n);
    R(k);
    for (int i = 1; i <= n; i++)
    {
        R(a[i]);
    }
    printf("%d\n", dp());
    return 0;
}

2.权值树状数组(需要离散化):

这种做法也用到了一个贪心:即修改k个数字,一定比修改少于k个数字更优

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string>
#include <string.h>
#include <cmath>
#define RT register
#define R(x) x = read()
#define For(i, j, n) for (int i = j; i <= n; ++i)
using namespace std;

const int N = 1e5 + 5;

int n, k;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}

int a[N], down_pre[N], up_len[N], down_len[N], en[N], id[N]; // id是反着做的辅助数组

void dp()
{
    // 正着做
    int res = 1;
    en[1] = a[1];
    up_len[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (a[i] >= en[res])
        {
            en[++res] = a[i];
            up_len[i] = res;
        }
        else
        {
            int pos = lower_bound(en + 1, en + res + 1, a[i]) - en;
            en[pos] = a[i];
            up_len[i] = pos;
        }
    }
    // for(int i = 1; i <= n; i++)
    //     cout << up_len[i] << " ";
    //  反着做
    res = 1;
    memset(en, 0, sizeof en);
    down_len[n] = 1;
    en[res] = -a[n];
    id[res] = n;
    for (int i = n - 1; i; i--)
    {
        int tmp = -a[i];
        if (tmp >= en[res])
        {
            down_pre[i] = id[res];
            en[++res] = tmp;
            id[res] = i;
            down_len[i] = res;
        }
        else
        {
            int pos = lower_bound(en + 1, en + res + 1, tmp) - en;
            en[pos] = tmp;
            down_len[i] = pos;
            down_pre[i] = id[pos - 1];
        }
    }
    /*for (int i = 1; i <= n; i++)
        cout << down_len[i] << " " << down_pre[i] << endl;*/
}

int tr[N], b[N], cnt;

int lowbit(int x)
{
    return x & (-x);
}

int query(int x)
{
    int res = -1;
    while (x)
    {
        res = max(res, tr[x]);
        x -= lowbit(x);
    }
    return res;
}

void add(int x, int k)
{
    for (int i = x; i <= n; i += lowbit(i))
        tr[i] = max(tr[i], k);
}

int solve()
{
    memcpy(b, a, sizeof(a));
    sort(b + 1, b + n + 1);
    cnt = unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; i++)
        a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;

    for (RT int i = 1; i <= n; i++)
        up_len[i] = query(a[i]) + 1, add(a[i], up_len[i]);
    memset(tr, 0, sizeof(tr));
    for (RT int i = n; i >= 1; i--)
        down_len[i] = query(n - a[i] + 1) + 1, add(n - a[i] + 1, down_len[i]);

    memset(tr, 0, sizeof(tr));
    int ans = 0;
    a[n + 1] = cnt + 1;
    for (int i = k + 2; i <= n + 1; i++)
    {
        add(a[i - k - 1], up_len[i - k - 1]);
        ans = max(ans, query(a[i]) + k + down_len[i]); // 注意这两行:a[i-k-1]和a[i],要理解权值树状数组的含义
    }
    return ans;
}

int main()
{
    R(n);
    R(k);
    if (k >= n - 1)
    {
        printf("%d\n", n);
        return 0;
    }
    for (int i = 1; i <= n; i++)
    {
        R(a[i]);
    }
    // dp();
    printf("%d\n", solve());
    return 0;
}

 

标签:down,en,int,res,pos,len,蓝桥,2022,P8776
From: https://www.cnblogs.com/smartljy/p/18112148

相关文章

  • 蓝桥杯单片机组第十四届省赛
     1.题目 本次题目模块较多,难度较大  2.底层代码2.1三个常用模块2.1.1按键本次省赛采用了NE555模块,要用P34引脚,在按键底层中,要把P34相关的代码注释掉,并且要将P34引脚用跳线帽(可以用超声波模块的跳线帽)与SIGNAL相连Key.c#include<Key.h>unsignedchar......
  • 蓝桥杯单片机第十三届省赛
    一、题目这次的省赛我感觉还是比较基础的,掌握几个模块就可以完全写出题目是在在网上找的二、代码 main.c#include<STC15F2K60S2.H>#include<ds1302.h>#include<onewire.h>#include<intrins.h>#include<Init.h>#include<Key.h>#include<Seg.h>#include......
  • VS2022+QT5.14.2开发VS QT Tool的使用
    1.安装环境vs2022+QT5.14.2qtvstool(vsaddin)的使用遇到的坑1.安装qt-vsaddin-msvc2022-3.0.2.vsix安装失败2.安装qt-vsaddin-msvc2022-2.8.0.vsix在qtSetting->qtmodels模块管理中,没有Selectmodel的功能选项如下图位置3.卸载版本vsaddin_2.8.0后安装qt-vsaddin-msvc2......
  • 蓝桥杯算法集训 - Week 5:树状数组、各类DP算法
    蓝桥杯算法集训-Week5本系列随笔用于整理AcWing题单——《蓝桥杯集训·每日一题2024》的系列题型及其对应的算法模板。一、树状数组树状数组是一种数据结构,可以快速地完成以下两个操作:将第i个数加上c快速求前缀和,即任意区间[i,j]的和Ⅰ、代码模板//树状数组长度......
  • 蓝桥杯javaB组备赛
    15届蓝桥杯备赛java语法基础IO框架importjava.util.*;importjava.IO.IOException;importjava.IO.BufferedReader;publicclassMain{publicstaticvoidmain(String[]args)throwsIOException{BufferedReaderreader=newBufferedReader(newInputStre......
  • 【蓝桥杯】小明发明了一种给由全大写字母组成的字符串编码的方法。对于每一个大写字母
    【问题描述】小明发明了一种给由全大写字母组成的字符串编码的方法。对于每一个大写字母,小明将它转换成它在26个英文字母中序号,即A→1,B→2,...Z→26。这样一个字符串就能被转化成一个数字序列:比如ABCXYZ→123242526。现在给定一个转换后的数字序列,小明想还原出原本的......
  • 蓝桥杯备考随手记: 字符串转换
    在Java中,字符串转换是指将一个数据类型的变量转换成字符串类型的操作。字符串转换可以通过以下几种方式实现:使用String类的valueOf()方法:该方法可以将任意数据类型转换成字符串类型。例如:intnum=10;Stringstr=String.valueOf(num);该方法还可以用于将字符数组转换......
  • smu2024蓝桥杯训练1
    A[语言月赛202401]装满葡萄汁的酒杯查看代码voidsolve(){intn;cin>>n;if(n<=100)cout<<100;elseif(n<=150)cout<<150;elseif(n<=300)cout<<300;elseif(n<=400)cout<<400;e......
  • 蓝桥杯练习笔记(十六)
    蓝桥杯练习笔记(十六)一、输入示例:312111347453这是用到了m叉树的结论:对于某个m叉树的一个节点n,假如其有完整子树,则其左子节点l为l=(n-1)m+2,右子节点r为r=mn+1。基于此我们可以快速判断这个数在某些节点处的具体情况。比如是否是满叶子等等。蓝桥官网题解:......
  • 蓝桥杯T5合根植物——并查集模板题
    5.合根植物-蓝桥云课(lanqiao.cn) #include<bits/stdc++.h>usingnamespacestd;intm,n,pre[1000000];set<int>s;intfind(intx){if(pre[x]==x)returnx;returnfind(pre[x]);}intmain(){//请在此输入您的代码cin>>m>>......