1. 程式人生 > >2019杭電多校第二場hdu6602 Longest Subarray(線段樹)

2019杭電多校第二場hdu6602 Longest Subarray(線段樹)

Longest Subarray

題目傳送門

解題思路

本題求一個最大的子區間,滿足區間內的數字要麼出現次數大於等於k次,要麼沒出現過。給定區間內的數字範圍是1~c。

如果r為右邊界,對於一種數字x,滿足條件的左邊界l的範圍是r左邊第一個x出現的位置+1(即這段區間內沒有出現過x,如果x在1~r內都沒有出現過,那麼1~r自然都是l的合法範圍),以及1到從右往左數數第k個x出現的位置(即這段區間內的x出現次數大於等於k)。所以我們只要找到同時是c種數字的合法左邊界的位置中最小的,然後列舉所有的i作為右邊界即可得出答案。

但是這樣直接寫肯定超時。所以我們用線段樹來維護每個位置可以作為多少種數字的合法範圍,以及一個區間內的最大值。在查詢的時候只要儘量往左子樹找就可以了。還有一個問題,難道我們要每次列舉都重新劃分範圍麼?肯定是不行的。所以我們記錄所有數字出現的位置,然後先讓n為右邊界,用線段樹維護好其合法範圍,再讓右邊界往左移,每次移動其實只是把舊的右邊界歸0,然後改變了舊的右邊界上的數字的合法範圍。我們已經記錄了數字的出現位置,利用這個,只需要線上段樹上進行幾次修改即可,因為當左邊界比右邊界小的時候會得到負數,不會使答案更新,所以不歸零也不會影響答案。

程式碼如下

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;

inline int read(){
    int res = 0, w = 0; char ch = 0;
    while(!isdigit(ch)){
        w |= ch == '-', ch = getchar();
    }
    while(isdigit(ch)){
        res = (res << 3) + (res << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -res : res;
}

const int N = 100005;

struct T{
    int l, r;
    int maxx, lazy;
}tree[N<<2];
int a[N];
vector<int> vec[N];

void build(int k, int l, int r)
{
    tree[k].l = l;
    tree[k].r = r;
    tree[k].lazy = tree[k].maxx = 0;
    if(l == r)
        return;
    int mid = (l + r) / 2;
    build(2*k, l, mid);
    build(2*k+1, mid + 1, r);
}

inline void push_down(int k)
{
    if(tree[k].lazy){
        tree[2*k].maxx += tree[k].lazy;
        tree[2*k+1].maxx += tree[k].lazy;
        tree[2*k].lazy += tree[k].lazy;
        tree[2*k+1].lazy += tree[k].lazy;   //+=
        tree[k].lazy = 0;
    }
}

void insert(int k, int l, int r, int u)
{
    if(l > r)
        return;
    if(tree[k].l >= l && tree[k].r <= r){
        tree[k].lazy += u;
        tree[k].maxx += u;
        return;
    }
    int mid = (tree[k].l + tree[k].r) / 2;
    push_down(k);
    if(l <= mid)
        insert(2*k, l, r, u);
    if(r > mid)
        insert(2*k+1, l, r, u);
    tree[k].maxx = max(tree[2*k].maxx, tree[2*k+1].maxx);
}

int n, c, k;

int query(int k)
{
    if(tree[k].maxx < c)
        return n + 1;
    if(tree[k].l == tree[k].r)
        return tree[k].l;
    push_down(k);
    if(tree[2*k].maxx == c)
        return query(2*k);
    else
        return query(2*k+1);
}

int main()
{
    while(scanf("%d%d%d", &n, &c, &k) != EOF){
        for(int i = 1; i <= n; i ++)
            a[i] = read();
        for(int i = 1; i <= n; i ++)
            vec[a[i]].push_back(i);
        build(1, 1, n);
        for(int i = 1; i <= c; i ++){
            if(vec[i].empty())
                insert(1, 1, n, 1);
            else {
                insert(1, vec[i][vec[i].size() - 1] + 1, n, 1);
                if(vec[i].size() >= k)
                    insert(1, 1, vec[i][vec[i].size() - k], 1);
            }
        }
        int ans = 0;
        for(int i = n; i >= 1; i --){
            ans = max(ans, i - query(1) + 1);
            if(vec[a[i]].size() >= k)
                insert(1, 1, vec[a[i]][vec[a[i]].size() - k], -1);
            vec[a[i]].pop_back();
            if(vec[a[i]].empty())
                insert(1, 1, i, 1);
            else {
                insert(1, vec[a[i]][vec[a[i]].size() - 1] + 1, i - 1, 1);
                if(vec[a[i]].size() >= k)
                    insert(1, 1, vec[a[i]][vec[a[i]].size() - k], 1);
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}