Codeforces Round #650 (Div. 3)

Codeforces Round #650 (Div. 3)


F1. Flying Sort (Easy Version)

题意:

给定长度为n的序列a,保证序列中的数两两不同
有两种操作:
1.选择序列中的一个数,将他移动到序列开头
2.选择序列中的一个数,将他移动到序列结尾

现在你可以进行无限次操作,问最少使用多少次操作能够使得序列递增

数据范围:n<=3e4,a(i)<=1e9

解法:

最优情况下每个数最多操作一次,
将问题转化为计算:n - 不需要操作的数量,求最大不需要操作数量即可

因为每次操作只能将一个数移到前面和后面,而不能移到中间,
所以最大不需要操作数量就是满足数字连续的最长子序列长度,
例如2 3 1 5 4,数字连续的最长子序列是2 3 4,长度为3,那么答案为5-3=2

完整解题步骤:
因为a(i)很大,所以先离散化
令d[i]表示以i为结尾的最大长度,那么转移方程:d[a[i]]=d[a[i]-1]+1
设d[i]的最大值为ma,则答案为n-ma

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=3e3+5;
int a[maxm];
int b[maxm];
int d[maxm];
signed main(){
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(b+1,b+1+n,a[i])-b;
        }
        for(int i=1;i<=n;i++){
            d[i]=0;
        }
        int ma=0;
        for(int i=1;i<=n;i++){
            d[a[i]]=d[a[i]-1]+1;
            ma=max(ma,d[a[i]]);
        }
        cout<<n-ma<<endl;
    }
    return 0;
}

F2. Flying Sort (Hard Version)

题意:

在F1的基础上,n的范围由3e4变为2e5,且序列a中的元素可以相同

解法:

基本思路还是一样,求最大不操作数量

但是现在数字会重复,且每次操作只能将一个数移到前面和后面,而不能移到中间,
所以如果我们选择的子序列的值域为[x,y],那么[x+1,y-1]的所有数都必须被选定,一个都不能少

令d[i]表示以a[i]为结尾的最大长度
last[a[i]]表示a[i]上一次出现位置
tot[a[i]]表示a[i]的总数量
cnt[a[i]]表示当前遇到的a[i]总数量
fir[a[i]]表示a[i]出现的第一个位置

转移:
d[i]=1;
d[i]=d[last[a[i]]+1;
d[i]=cnt[a[i]-1]+1;
if(cnt[a[i]-1]==tot[a[i]-1]){
    d[i]=d[fir[a[i]-1]]+tot[a[i]-1];
}
对所有取max

ps:
d[i]=d[last[a[i]-1]]+1;是不允许的
因为只有a[i]-1 全部出现在中间才能这样
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
int a[maxm];
int b[maxm];
int d[maxm];
int tot[maxm];
int cnt[maxm];
int fir[maxm];
int last[maxm];
signed main(){
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        int num=unique(b+1,b+1+n)-b-1;
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(b+1,b+1+num,a[i])-b;
        }
        for(int i=1;i<=num;i++){
            tot[i]=cnt[i]=fir[i]=last[i]=0;
        }
        for(int i=1;i<=n;i++){
            tot[a[i]]++;
        }
        int ma=0;
        for(int i=1;i<=n;i++){
            //
            d[i]=1;
            d[i]=max(d[i],d[last[a[i]]]+1);//从上一个a[i]转移
            d[i]=max(d[i],cnt[a[i]-1]+1);//前面接已有的全部a[i]-1
            //不能从d[last[a[i]-1]]转移是因为必须a[i]-1全部出现在中间
            if(cnt[a[i]-1]==tot[a[i]-1]){//如果a[i]-1全部出现,则可以从第一个a[i]-1转移
                d[i]=max(d[i],d[fir[a[i]-1]]+tot[a[i]-1]-1+1);
            }
            //
            if(!fir[a[i]]){
                fir[a[i]]=i;
            }
            cnt[a[i]]++;
            last[a[i]]=i;
            ma=max(ma,d[i]);
        }
        cout<<n-ma<<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值