zoukankan      html  css  js  c++  java
  • 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 - ans);
        }
        return 0;
    }
    View Code

    做法二:(乱搞)

    #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
    ",ans);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    终端服务器超出了最大允许连接数
    获得拼凑SQL语句运行后的结果
    无法添加此项,原因是要将其添加到的项不是解决方案文件夹
    SQL SERVER读书笔记:内存
    复制DropDownList
    设计模式基础
    WebForm与MVC混用
    SQL SERVER读书笔记:JOIN
    SQL SERVER读书笔记:nolock
    SQL SERVER读书笔记:阻塞与死锁
  • 原文地址:https://www.cnblogs.com/InitRain/p/12543640.html
Copyright © 2011-2022 走看看