codeforces1437 E. Make It Increasing——最长上升子序列

E. Make It Increasing

首先让 a i = a i − i a_i=a_i-i ai=aii这样可以是严格单增变成单调增。
参考官方题解
首先不难得出如果我们根据不同修改的位置分割成若干段,那么若干段是互不影响的,我们只需要求出每一个若干段修改次数的最小值。

如果当前考虑l~r这一段,这里l和r都是不能修改的位置,我们要使得 a l ≤ a l + 1 → r − 1 ≤ a r a_l\leq a_{l+1\to r-1}\leq a_r alal+1r1ar,对于原来不在此范围内的数一定需要修改,所以只需要考虑在此范围内的数如何修改的最少!实际上是选择最长的上升子序列,剩下的修改即可。而最长上升子序列我们可以O(nlogn)解决。

#define IO ios::sync_with_stdio(false);cin.tie();cout.tie(0)
#pragma GCC optimize(2)
#include<set>
#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<random>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=500010;
int a[N],b[N],n,k;
int main()
{
    IO;
    int T=1;
    //cin>>T;
    while(T--)
    {
        cin>>n>>k;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>b[i];
        a[0]=-1e9,a[n+1]=2e9;
        for(int i=0;i<=n+1;i++) a[i]-=i;
        b[k+1]=n+1;
        b[0]=0;
        bool ok=1;
        int res=0;
        for(int i=0;i<=k;i++)
        {
            int l=b[i],r=b[i+1];
            if(a[l]>a[r])
            {
                ok=0;
                break;
            }
            vector<int> lis;
            for(int j=l+1;j<r;j++)
                if(a[l]<=a[j]&&a[j]<=a[r])
                {
                    auto pos=upper_bound(lis.begin(),lis.end(),a[j]);
		        	if (pos==lis.end()) lis.push_back(a[j]);
			        else *pos=a[j];//这里会修改值
                }
            res+=(r-l-1)-lis.size();
        }
        if(!ok) res=-1;
        cout<<res<<'\n';
    }
    return 0;
}

反思:md我知道那个小trick使得严格变成非严格,但是还是没能运用,还是见的太少,没运用这个小trick导致我的判断不合法时尤为困难。

题解中的最长上升子序列代码好简洁,学习了!
贪心,考虑当前的数,那么替换第一个大于它的数并不影响原来维护序列的单调性,而且答案不会更差!
要加油哦~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值