zoukankan      html  css  js  c++  java
  • T1飞跃树林 && 【最长等差子序列】

    solution by Mr.gtf

    一道简单的递推
    首先我们对树高从大到小排序
    很容易得到递推式
    ans[i]=Σans[j] (j<i && h[j]-h[i]<=K)
    朴素做法暴力枚举,暴力for j from 1 to i-1
    聪明一点能发现满足条件的j一定是i之前连续的一段,只要从i-1开始向前for到不满足为止
    这样做最坏情况依然是O(nK)或者O(n^2)
    事实上,只要再深入思考,就能发现,对于每个i,满足条件的最小的j是单调不降的。

    所以我们只需要准备一个队列,在求i答案时将队列首不满足条件的j踢出,在i求出答案后入队,需要维护一个队列内的下标的ans和。
    复杂度O(n)

    My Code

    #define N 1000010
    #define MOD 1000000007
    
    using namespace std;
    
    int n , a[N] , f[N] , k;
    
    int z[N] , head , tail , done;
    
    int main()
    {
    //  freopen("tree.in" , "r" , stdin);
    //  freopen("tree.out" , "w" , stdout);
        scanf("%d%d",&n,&k);
        for (int i = 1 ; i <= n ; i++)
            cin >> a[i];
        sort(a + 1 , a + 1 + n);
        a[0] = 0;
        f[0] = 1;
        done = 1;
        for (int i = 1 ; i <= n ; i++)
        {
            while(a[z[head]] + k < a[i] && z[head] < i)
            {
                head++;
            }
            f[i] = done;
            z[++tail] = i;
            done += f[i];
            done %= MOD;
            /*for (int j = i - 1 ; j >= 0 ;j--)   //不优化40分
            {
                if(a[i] - a[j] <= k)
                {
                    f[i] += f[j];
                    f[i] %= MOD;
                }
                else
                    break;
            }*/
        }
        printf("%d
    " , f[n]);
        return 0;
    }
    

    Teacher's Code

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 1e6 + 50;
    const int MOD = 1e9 + 7;
    int n, K, h[MaxN];
    int ans[MaxN];
    
    struct Queue
    {
        const static int MAX_SIZE = 1e6;
        int data[MAX_SIZE];
        int head, tail;
    
        Queue() : head(0), tail(0) {}
    
        inline static int inc(int p)
        {
            ++p;
            return (p == MAX_SIZE) ? 0 : p;
        }
    
        void push(int x);
    
        void pop();
    
        int front();
    
        inline bool empty();
    };
    
    Queue q;
    
    int main()
    {
        freopen("tree.in", "r", stdin);
        freopen("tree.out", "w", stdout);
    
        scanf("%d%d", &n, &K);
        for (int i = 0; i < n; ++i) scanf("%d", &h[i]);
        sort(h, h + n, greater<int>());
        h[n] = 0;
        ans[0] = 1;
        q.push(0);
        int qSum = ans[0];
    
        for (int i = 1; i <= n; ++i) {
            while (!q.empty()) {
                int j = q.front();
                if (h[j] - h[i] > K) {
                    qSum = ((qSum - ans[j]) % MOD + MOD) % MOD;
                    q.pop();
                } else break;
            }
            ans[i] = qSum;
            q.push(i);
            qSum = (qSum + ans[i]) % MOD;
        }
    
        printf("%d
    ", ans[n]);
        return 0;
    }
    //队列操作
    void Queue::push(int x)
    {
        if (inc(tail) == head) throw x;
        data[tail] = x;
        tail = inc(tail);
    }
    
    void Queue::pop()
    {
        if (empty()) throw head;
        head = inc(head);
    }
    
    int Queue::front()
    {
        if (empty()) throw head;
        return data[head];
    }
    
    inline bool Queue::empty()
    {
        return head == tail;
    }
    

    【例】最长等差子序列

    题目描述

    给你一个以正整数构成的序列,求它的最长等差子序列的长度
    难度1 n<=1000,a[i]<=1000
    难度2 n<=100,a[i]<=10^9

    解析

    首先离散化!

    注意到难度二n只有100,而a很大

    大致就是把一个数组如
    a 111 44 76538 0 342566677

    转成
    a 3 2 4 1 5
    再调用

    然后发现这题与上题的区别就在于 公差不定 和 必须等差

    公差不定其实就枚举公差,再重复上个题的操作

    必须等差就把上个题的判断条件改掉(h[j] - h[i] > K 改成 h[j] - h[i] == k)

    复杂度

    公差个数nn
    难度一:1000 * 1000 * 1000
    难度二:10^9 ^ 100 ^ 100
    第一层循环公差,第二层循环序列右端点,第三层循环上一个元素的下表
    优化:把第三层循环用数组存起来
    f[i]:到第i个元素的最长等差序列
    k:公差
    (f[k][i] = max{f[k][j] | a[j] + k == a[i]})
    定义p[k][a[j]] = max{f[k][j] | a[j] + k = a[i]}
    就能把O(n)的循环变成 O(1)的查询
    写的时候可以滚掉一维公差k,省空间

    注意负公差

  • 相关阅读:
    Python DB API 连接数据库
    PHP base64多图片上传
    Linux vim编写程序时出现高亮字符,如何取消?
    CDN,内容分发网络。
    MySQL随机取数据
    tp5 快速接入扫码支付
    tp5定时器
    清空测试数据
    Centos Crontab查看状态和开启
    select2 使用
  • 原文地址:https://www.cnblogs.com/ZhengkunJia/p/12228459.html
Copyright © 2011-2022 走看看