LOJ10121 与众不同

戏子无情 提交于 2020-01-27 02:55:58

## \[ 本题要求: 求 [l,r] 内最长连续无重子序列(下称LCRS) \]

  • 如何解决

    st[i] 记录以i结尾的LCRS的起点)last[i] 记录 i 在 a[] 中上次出现的位置
    p.s. st[]在任何一个序列中都是单调不上升的,有兴趣的读者可以自己推推看

  • 怎么做
  1. 处理\(last[i]\);
  2. 维护\(st[i]\)转移:$ st[i] = max(st[i-1],last[a[i]]+1) $
  3. 所以\(f[i][0]=i-st[i]+1\)
  4. 所以可以预处理\(RMQ\)
  5. 对每一次询问的\(L,R寻找其pos(pos的左侧所有的st[i]<l,右侧都 >=l)\)
  6. 所以 $ answer = max(pos-l,RMQ(pos,r))$
    p.s. 因为st是单调的,所以可以用二分找pos
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2 * 1e6 + 5;
const int adnu = 1e6 + 5;
const int logn = 50;
int n, m;
int last[maxn], st[maxn], a[maxn], len[maxn];
int f[maxn][logn], lg[maxn];
void RMQ() {
    lg[0] = -1;
    for (int i = 1; i <= n; i++) {
        f[i][0] = len[i];
        lg[i] = lg[i / 2] + 1;
    }
    for (int j = 1; j <= lg[n] + 1; j++) {
        for (int i = 1; i + (1 << j) - 1 <= n; i++) { 
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        }
    }
}
int bis(int l, int r) {
    if(l == r) return l;
    int x = l, y = r;
    while (x <= y) {
        int mid = x + (y-x) / 2;
        if (st[mid] < l)
            x = mid + 1;
        else
            y = mid - 1;
    }
    return x; 
}
int rmq(int x, int y) { return max(f[x][lg[y - x + 1]], f[y - (1 << lg[y - x + 1]) + 1][lg[y - x + 1]]); }
void solve(int l, int r) {
    if (st[r] <= l)
        printf("%d\n", r - l + 1);
    else if (st[l] == l)
        printf("%d\n", rmq(l, r));
    else {
        int pos = bis(l, r);
        printf("%d\n", max(pos - l, rmq(pos, r)));
    }
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        a[i] += adnu;
        st[i] = max(st[i - 1], last[a[i]] + 1);
        len[i] = i - st[i] + 1;
        last[a[i]] = i;
    }
    RMQ();
    for (int i = 1; i <= m; i++) {
        int l,r;
        scanf("%d%d", &l, &r);
        l++;
        r++;
        solve(l, r);
    }
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!