• CSP模拟52联测14 C.天竺葵


    CSP模拟52联测14 C.天竺葵

    题目大意

    给定两个长度为 n n n 的序列 a , b a , b a,b

    需要在 a a a 序列中好到最长的序列 c c c 满足 c i + 1 > b i × c i c _{i + 1} > b_i \times c_i ci+1>bi×ci

    输出长度

    1 ≤ n ≤ 1 0 6 1\le n\le 10^6 1n106

    思路

    感觉和 n ( log ⁡ n ) n(\log n) n(logn) 求最长上升子序列差不多

    考虑 d p dp dp

    d p i dp_i dpi 为第 c i c_i ci 的最小值

    朴素的转移 O ( n 2 ) O(n^2) O(n2)

    70分代码

    #include 
    #define fu(x , y , z) for(int x = y ; x <= z ; x ++)
    
    #define LL long long
    
    using namespace std;
    const int N = 1e6 + 5;
    const LL inf = 1e12 + 5;
    int n , ans , ans1 , lis[N] , len;
    LL f[N] , a[N] , b[N] , min1;
    int fans (int l , int r , LL x) {
        if (l == r) {
            return a[lis[l]] > x ? l : inf;
        }
        else {
            int mid = l + r >> 1;
            if (a[lis[mid]] >= x) return min (mid , fans (l , mid , x));
            else return fans (mid + 1 , r , x);
        }
    }
    int main () {
        freopen ("C.in" , "r" , stdin);
        freopen ("C.out" , "w" , stdout);
        int flg = 0 , x;
        scanf ("%d" , &n);
        fu (i , 1 , n) scanf ("%lld" , &a[i]);
        fu (i , 1 , n) {
            scanf ("%lld" , &b[i]);
            if (b[i] != 1) flg = 1;
        }
        if (!flg) {
            lis[len = 1] = 1;
            fu (i , 2 , n) {
                if (a[lis[len]] < a[i]) {
                    lis[++len] = i;
                    continue;
                }
                x = fans (1 , len , a[i]);
                lis[x] = i; 
            }
            printf ("%d" , len);
            return 0;
        }
        ans = 1 , f[1] = a[1];
        min1 = a[1];
        fu (i , 2 , n) f[i] = 1e12 + 5;
        fu (i , 2 , n) {
            ans1 = 0;
            fu (j , 1 , ans + 1) {
                if (a[i] > f[j - 1] * b[j - 1]) {
                    f[j] = min (f[j] , a[i]);
                    ans1 = max(ans1 , j);
                }
            }
            min1 = min (min1 , a[i]);
            ans = max (ans , ans1);
        }
        printf ("%d" , ans);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    我们发现是转移的时候太慢了。

    假设当前要处理的是 a i a_i ai

    我们把现在的 d p dp dp 分成两部分前 k k k 个是小于 a i a_i ai 的,后 k k k 个是大于 a i a_i ai

    因为 d p 1 → k ≤ a i dp_{1\to k} \le a_i dp1kai ,所以 $min_{j = 1 }^k (dp_j ,a_i)= dp_i $

    因为 d p k + 1 → l e n > a i dp_{k + 1 \to len} > a_i dpk+1len>ai ,所以 a i < d p j ∗ b j ( j ∈ [ k + 1 , l e n ] ) a_i < dp_j *b_j (j\in[k + 1 , len]) ai<dpjbj(j[k+1,len])

    所以现在要更新的就只有 d p k dp_k dpk

    直接遍历一遍,二分查找 k k k 就好了

    时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

    code

    #include 
    #define fu(x , y , z) for(int x = y ; x <= z ; x ++)
    #define LL long long
    using namespace std;
    const int N = 1e7 + 5;
    const LL inf = 1e12 + 5;
    int n , ans , ans1 , lis[N] , len;
    __int128 f[N] , a[N] , b[N] , min1;
    int fans (int l , int r , LL x) {
        if (l == r) {
            return a[lis[l]] > x ? l : inf;
        }
        else {
            int mid = l + r >> 1;
            if (a[lis[mid]] >= x) return min (mid , fans (l , mid , x));
            else return fans (mid + 1 , r , x);
        }
    }
    int main () {
        freopen ("C.in" , "r" , stdin);
        freopen ("C.out" , "w" , stdout);
        LL x;
        scanf ("%d" , &n);
        fu (i , 1 , n) scanf ("%lld" , &x) , a[i] = x;
        fu (i , 1 , n) scanf ("%lld" , &x) , b[i] = x;
        int k;
        ans = 1 , f[1] = a[1];
        min1 = a[1];
        fu (i , 2 , n) {
            k = lower_bound(f + 1 , f + ans + 1 , a[i]) - f - 1;
            if (k == ans) {
                if (a[i] > b[ans] * f[ans]) f[++ans] = a[i];
            }
            else {
                if (a[i] > b[k] * f[k]) f[k + 1] = a[i];
            }
        }
        printf ("%d" , ans);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
  • 相关阅读:
    java数据库编程
    力扣算法篇:连续子数组的最大和
    win11和虚拟机上的ubuntu系统共享文件夹
    华为OD机试 - 等和子数组最小和 - 深度优先搜索(Java 2022 Q4 100分)
    AXI协议详解(7)-响应信号
    MATLAB R2022b遇到“License Manager Error -8”怎么解决
    Python教程(13)——Python运算符详解。算术运算符|比较运算符|逻辑运算符|位运算符
    云原生:Docker 实践经验(七)-容器与虚拟化
    为什么你懂很多道理,偏偏就是不会赚钱?
    spring boot单元测试之druid NullPointException问题解决
  • 原文地址:https://blog.csdn.net/fzyyyyyyyy/article/details/133779260