• CF407E k-d-sequence


    思路

    要素察觉:必须要是一个公差为 (d) 的等差数列。

    特判

    首先要特判掉 (d=0) 的情况,这样的情况下就是要寻找最长的一段数字相同的区间,找到之后输出左右端点即可(可以 (O(n)) 扫一遍)。

    其他情况

    再来看别的情况,对于一个区间 ([l,r]),如果要满足是一个公差为 (d) 的等差序列,那么:

    • 这个等差数列里的所有数 (mod d) 的结果应该一样.
    • 区间内没有重复的数.

    现在考虑如何实现上述条件:

    • 首先将序列分成若干个 (x mod d) 都一样的子区间。

      在从左往右扫的过程中,如果遇到了与前面 (xmod d) 的值不同的数,就将左边 (xmod d) 值相同的数作为一个独立的区间来处理,这样就可以把序列分成若干个 (x mod d) 都一样的区间。

    • 对于一个满足 (xmod d=c) 的数列,把所有的 (x) 变成 (dfrac{x-c}{d}) (因为整形的性质,可以直接除)。

      这一步称之为归一化,实现了将一个区间的的公差化为 (1)

      问题就转化成了加入 (k) 个数,使区间排序后公差为 (1)

    • 对于一个区间 ([L,R]),算出最少加几个数。

      需要满足的条件显然就是不能有重复,那么最少加的数的个数就是 (max(L,R)-min(L,R)+1-(R-L+1))

    • 从小到大枚举 (R)

      那么问题就相当于求最小的 (L),使得

      • ([L,R]) 无重复。

        从小到大枚举 (R),对于新的 (a[R]) 很容易知道它前面一个和他相等的数 (a[T]) (可以用(map)实现),那么 (L) 至少要大于 (T)

      • 加的数不能多于 (k) 个。

        也就是 (max(L,R)-min(L,R)+1-(R-L+1)le k),转化一下即 (max(L,R)-min(L,R)+Lle k + R)

        用线段树维护 (w[L]=max(L,R)-min(L,R)+L)

        假如 (L) 的下界是 (T),那么我们要在 ([T+1,R]) 中找最左的位置使得 (wle k + R)

    如果能完成上述过程,这道题就做完了,那么现在的问题是,如何维护 (w)

    维护 (w)的方法

    用单调栈。维护一个单调递减的栈和一个单调递增的栈,两个栈的维护方法是同理的,此处以单调递减的栈为例进行讲解。

    因为单调栈递减的性质,所以当一个大于栈顶的元素加入时,会不断地弹出栈顶,直到栈顶元素大于此元素为止,再将此元素入栈,由此,单调栈可以将 (max(L,R)) 分成递减的若干段,考虑是如何实现的:

    1. 如图所示,假设有一个单调递减的单调栈,其中 (S1> S2> S3)(S3) 为栈顶元素。

      1

    2. 由于单调栈的性质,(S1) 和栈中上一个元素之间可能是有别的元素的,所以 (S1,S2,S3) 其实代表了三个区间的最大值。

      2

    3. 此时,单调栈中来了一个新的元素 (a),显然 (a>S1>S2>S3),为了维护单调栈的性质,所以我们要将 (S1,S2,S3) 弹栈。

      3

    4. 在弹栈时,因为此时这三个元素的值不能代表上述三个段的最大值了,所以我们需要将三个段的贡献从线段树中减去。

      4

    5. 同时,新的元素 (a) 就加进来了,这个时候就可以发现原来三个段的代表元素被弹出之后,其实就被新元素所接管了,整个区间的最大值变成 (a) 元素的值,所以再对一整个区间进行区间加操作。

      5

    这样单调栈就实现了将 (max(L,R)) 分成了递减的若干段,由此就可以不断进行段树的区间加、区间减。

    (min) 的维护与 (max) 同理。

    总时间复杂度 (O(nlog n))

    代码

    /*
    Author:loceaner
    线段树,单调栈 
    */
    #include <map>
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define lson rt << 1
    #define rson rt << 1 | 1
    using namespace std;
    
    const int A = 2e5 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9;
    const int inf = 0x3f3f3f3f;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    bool flag;
    map <int, int> last;
    int sl[A], topl, sr[A], topr;
    struct tr { int l, r, lazy, w; } t[A << 2];
    int n, k, d, ansl = 1, ansr = 1, a[A], ans;
    
    inline void pushup(int rt) {
      t[rt].w = min(t[lson].w, t[rson].w);
    }
    
    inline void calc(int rt, int x) {
      t[rt].lazy += x, t[rt].w += x;
    }
    
    inline void pushdown(int rt) {
      if (t[rt].lazy && t[rt].l < t[rt].r) {
        calc(lson, t[rt].lazy), calc(rson, t[rt].lazy);
        t[rt].lazy = 0;
      }
    }
    
    void build(int rt, int l, int r) {
      t[rt].l = l, t[rt].r = r, t[rt].lazy = 0, t[rt].w = 0;
      if (l == r) { t[rt].w = l; return; }
      int mid = (l + r) >> 1;
      build(lson, l, mid), build(rson, mid + 1, r);
      pushup(rt);
    }
    
    void insert(int rt, int l, int r, int k) {
      if (l <= t[rt].l && t[rt].r <= r) return calc(rt, k);
      pushdown(rt);
      int mid = (t[rt].l + t[rt].r) >> 1;
      if (l <= mid) insert(lson, l, r, k);
      if (r > mid) insert(rson, l, r, k);
      pushup(rt);
    }
    
    int work(int rt, int k) {
      if (t[rt].l == t[rt].r) return t[rt].l;
      pushdown(rt);
      if (t[lson].w <= k) return work(lson, k);
      else return work(rson, k);
    }
    
    void query(int rt, int l, int r, int k) {
      if (l <= t[rt].l && t[rt].r <= r) {
        if (t[rt].w <= k) flag = 1, ans = work(rt, k);
        return;
      }
      pushdown(rt);
      int mid = (t[rt].l + t[rt].r) >> 1;
      if (l <= mid && !flag) query(lson, l, r, k);
      if (r > mid && !flag) query(rson, l, r, k);
    }
    
    void del(int rt, int x) {
      if (x < t[rt].l || x > t[rt].r) return;
      pushdown(rt);
      if (t[rt].l == x && t[rt].r == x) { t[rt].w = 0; return; }
      del(lson, x), del(rson, x);
      pushup(rt);
    }
    
    int main() {
      n = read(), k = read(), d = read();
      for (int i = 1; i <= n; i++) a[i] = read() + inf;
      if (d == 0) {
        for (int i = 1; i <= n; i++) {
          int l = i;
          while (a[i] == a[i + 1] && i < n) i++;
          if (ansr - ansl < i - l) ansl = l, ansr = i;
        }
        cout << ansl << ' ' << ansr << '
    ';
        return 0;
      }
      build(1, 1, n);
      //枚举右端点 
      for (int i = 1, L = 1; i <= n; i++) {
        int tmp = L;
        if (a[i] % d == a[i - 1] % d) L = max(L, last[a[i]] + 1);
        else L = i;
        last[a[i]] = i;
        while (tmp < L) del(1, tmp++);
        //递增栈 
        while (topl && sl[topl] >= L && a[sl[topl]] >= a[i]) {
          insert(1, max(L, sl[topl - 1] + 1), sl[topl], a[sl[topl]] / d);
          topl--;
        }
        insert(1, max(L, sl[topl] + 1), i, -a[i] / d);
        sl[++topl] = i;
        //递减栈
        while (topr && sr[topr] >= L && a[sr[topr]] <= a[i]) {
          insert(1, max(L, sr[topr - 1] +1), sr[topr], -a[sr[topr]] / d);
          topr--;
        }
        insert(1, max(L, sr[topr] + 1), i, a[i] / d);
        sr[++topr] = i;
        flag = 0, ans = 0;
        query(1, L, i, k + i); //思路中的式子 
        if (ansr - ansl < i - ans) ansl = ans, ansr = i;
      }
      cout << ansl << " " << ansr << '
    ';
      return 0;
    }
    
  • 相关阅读:
    练习写一个工资结算系统
    【课堂】模拟奥特曼打小怪兽
    模拟简单对打(昨天代码的小修改)
    模拟简单游戏创建类
    数组的应用练习
    Java基础的思维导图
    springBoot集成MyBatis和Mybatis自动生成代码GeneratorMapper.xml配置
    ubuntu14.04安装eclipse没有标题
    ubuntu14.04安装Hadoop0.20.2
    Apache编码问题
  • 原文地址:https://www.cnblogs.com/loceaner/p/13485329.html
Copyright © 2020-2023  润新知