cf 1257 E. The Contest(好题)(LIS/思维)

题意:

有三个序列,a、b、c,每次操作可以把一个序列中的一个数移动到另一个序列中,

问,最少操作几次后,可以使得 a 序列里的所有数 小于 b 里面的所有数,b 里面的小于 c 里面的。

数字不重复,总共2e5的数据量。

思路:

做法一:(LIS)

这个做法是网上看到的,确实挺巧妙的,用这个方法,即使以后来个100个序列的,也不用怕了。

分别对a b c 排序,然后合并,

求最大上升子序列,然后上升子序列里的数不动,只移动非序列里的,答案就是 n - len,len最大,答案就最小。

求LIS的时候,要用树状数组优化一下,这样复杂度才能是 O(n*logn )。

做法二:(乱搞)

这个是自己瞎想想出来的,复杂度O(n),不过只适用于三个序列的。

三个序列里的,看做三种颜色记作颜色c1、c2、c3,然后混在一起排序,

然后每次固定一个左端点 l,表示移动到最后 1~l 为序列a里的数,查找一个可以使操作最小的右端点 r ,表示到最后 l+1~r 为序列b里的数,那么剩下的 r+1~n 就是序列c里的数。

怎么找操作最小的右端点r呢,先假设l不存在,也就是我们现在只把序列分为两段,

当右端点为 i 时,显然,操作数就是 1~i 的c3的数量 加上 i+1~n的 c2的数量,(这个就是需要对序列b c操作的次数)(对a操作的次数很好求,就是不在1~l 里的c1的数量)

为什么可以假设 l 不存在,因为当 l 移动的时候,如果吞掉一个c3,那么,r 在 l + 1 ~ n 每个点时的操作数就是全部减一,所以最小值的位置不变,只是值减一而已。

只分两段的时候,我们就很容易求 r 在 j ~ n时的最小值,这个就从后往前更新一下就好了。

所以,简而言之,就是预处理 j~n 的 r 的最小的位置在哪里,然后枚举 l ,更新答案。

(每次思维题我思路都讲得乱七八糟的。。。)

代码:

做法一:(LIS)

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include <queue>
#include <functional>
using namespace std;
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
int dp[maxn],mx[maxn];
int lowbit(int i){
    return i & (-i);
}
void insert(int i,int x,int n){
    while(i <= n){
        mx[i] = max(mx[i],x);
        i += lowbit(i);
    }
}
int _find(int i){
    int res = 0;
    while(i > 0){
        res = max(mx[i],res);
        i -= lowbit(i);
    }
    return res;
}

int LIS(int a[],int n){
    int res = 0;
    memset(mx,0,sizeof(mx));
    memset(dp,0,sizeof(dp));
    for(int i = 1;i <= n;i++){
        dp[a[i]] = _find(a[i]) + 1;
        res = max(res,dp[a[i]]);
        insert(a[i],dp[a[i]],n);
    }
    return res;
}
int a[maxn],b[maxn];
int main(){
    int _n[3],n;
    while(scanf("%d%d%d",&_n[0],&_n[1],&_n[2]) != EOF){
        n = 0;
        int cnt = 0;
        for(int k = 0;k < 3;k++){
            for(int i = 1;i <= _n[k];i++)
                scanf("%d",&b[i]);
            sort(b + 1,b + _n[k] + 1);
            for(int i = 1;i <= _n[k];i++)
                a[++cnt] = b[i];
            n += _n[k];
        }

        int ans = LIS(a,n);
        printf("%d\n",n - ans);
    }
    return 0;
}

做法二:(乱搞)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <cmath>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
int sum2[maxn],sum3[maxn],mi_p[maxn],mi[maxn];
int vis[maxn];
int main(){
    int n1,n2,n3,x,n;
    while(scanf("%d%d%d",&n1,&n2,&n3) != EOF){
        memset(vis,0,sizeof(vis));
        n = n1 + n2 + n3;
        for(int i = 1;i <= n1;i++){
            scanf("%d",&x);
            vis[x] = 1;
        }
        for(int i = 1;i <= n2;i++){
            scanf("%d",&x);
            vis[x] = 2;
        }
        for(int i = 1;i <= n3;i++){
            scanf("%d",&x);
            vis[x] = 3;
        }

        sum3[0] = 0;
        for(int i = 1;i <= n;i++){
            sum3[i] = sum3[i - 1];
            if(vis[i] == 3)
                sum3[i]++;
        }
        sum2[n + 1] = 0;
        for(int i = n;i >= 1;i--){
            sum2[i] = sum2[i + 1];
            if(vis[i] == 2)
                sum2[i]++;
        }

        int mival = sum3[n],ans = 1e9;
        mi[n] = sum3[n];
        for(int i = n - 1;i >= 1;i--){
            mi[i] = mi[i + 1];
            if(mival > sum3[i] + sum2[i + 1]){
                mival = sum3[i] + sum2[i + 1];
                mi[i] = mival;
            }
        }
        mi[0] = min(sum2[1],mi[1]);

        int one = 0,sub = 0,res;
        ans = n2 + n3;
        for(int i = 0;i < n;i++){
            if(vis[i] == 1)
                one++;
            if(vis[i] == 3)
                sub++;
            res = (i - one) + mi[i] - sub + (n1 - one);
            if(res < ans)
                ans = res;
        }
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/InitRain/p/12543640.html

时间: 2024-10-18 17:45:45

cf 1257 E. The Contest(好题)(LIS/思维)的相关文章

CF#FF(255)-div1-C【水题,枚举】

[吐槽]:本来没打算写这题的题解的,但惨不忍睹得WA了13次,想想还是记录一下吧.自己的“分类讨论能力”本来就很差. 刚开始第一眼扫过去以为是LIS,然后忽略了复杂度,果断TLE了,说起来也好惭愧,也说明有时候太懒得动脑了,总是习惯利用惯性思维,这不是一件好事. [题意]:给你大小为n的整型数组a[n],求这数组的一个子串,其中最多可以修改子串中的一个数字,使得到的子串是最长的严格递增的子串,输出该子串的长度 L. [思路]:O(n)复杂度,枚举断点情况.第0个和第n个位置默认为断点.(用ve[

hdu 4932 /bestcoder B题 #4 /思维题

题意:给一个数列(整数),用一些不相交的区间去覆盖(只能是用端点去覆盖,端点可以交).而且区间出度相等.求最大区间长度. 开始一下就敲了,枚举每个区间长度,判断合法,更新最大.但是后来一看小数,感觉不行,改为二分,后来还是挂了... 赛后才知道,二分的时候,答案必需要满足单调性啊,这里小的数据不行,大的数据可以行!如 0 1 5 6 10, 3不行,4行. 后来才知道,枚举时,每个差值的一半也是可以的:仔细想想很容易证明.(水,坑) #include<iostream> #include<

Codeforces Round #625 (Div. 2, based on Technocup 2020 Final Round) A. Contest for Robots(思维题)

Polycarp is preparing the first programming contest for robots. There are nn problems in it, and a lot of robots are going to participate in it. Each robot solving the problem ii gets pipi points, and the score of each robot in the competition is cal

CF#345 div2 A\B\C题

A题: 贪心水题,注意1,1这组数据,坑了不少人 #include <iostream> #include <cstring> using namespace std; int main() { int a1,a2; while(cin>>a1>>a2) { int i=0; int b = max(a1,a2); int s = min(a1,a2); if(b==1 && s==1) { cout<<0<<endl

Northwestern European Regional Contest 2017-I题- Installing Apps题解

一.题意 有一个手机,容量为$C$,网上有$N$个app,每个app有个安装包大小$d_i$,有个安装后的占用空间大小$s_i$,安装app是瞬间完成的,即app的占用空间可以瞬间由$d_i$变成$s_i$,而不需要其他多余的空间.问这个手机最多可以安装多少个app,输出最多可以安装的app数量和安装顺序. 二.思路 很显然的$dp$.按照$max(d,s)-s$从大到小排序.$dp[i][j]$表示在前$i$个app中,占用空间不超过$j$的条件下最多可以安装的app的数量.那么,有如下递推式

Bet(The 2016 ACM-ICPC Asia China-Final Contest 思路题)

题目: The Codejamon game is on fire! Fans across the world are predicting and betting on which team will win the game. A gambling company is providing betting odds for all teams; the odds for the ith team is Ai :Bi . For each team, you can bet any posi

2018-2019 ACM-ICPC, Asia Shenyang Regional Contest(补题)

A题 - Sockpuppets 未补 B题 - Sequences Generator 未补 C题 - Insertion Sort 签到题之一,排列计数. 题意:给你排列的长度$n$,要求你求出排列的个数,满足对其前k项排序后其最长上升子序列至少为$n-1$. 解决:当最长上升子序列为$n$时答案明显时$k!$,为$n-1$时,相当于将$n$长的有序序列中某一个位置的数插到另一个位置去,但因为会对前$k$个排序,所以在挪动时要保证前$k$个有序即可.接下来你可以暴力求出这个值,也可以手推,这

CF 277.5 A.SwapSort 水题

//STL教你做人系列 #include<stdio.h> #include<iostream> #include<math.h> #include<algorithm> using namespace std; int n,a[3100]; int main() { cin>>n; for(int i=0;i<n;i++) cin>>a[i]; cout<<n<<endl; for(int i=0;i&

CTU Open Contest 2017 Go Northwest!(思维题+map)

题目: Go Northwest! is a game usually played in the park main hall when occasional rainy weather discourages the visitors from enjoying outdoor attractions. The game is played by a pair of players and it is based entirely on luck, the players can hardl