Luogu CF484E Sign on Fence

主席树 + 二分答案。

容易想到二分答案转化为判定可行性的问题,假设每一次询问的区间是$[x, y]$,长度为$k$,那么假设当前二分到$mid$,我们把原序列中所有大于等于$mid$的值都记一个$1$,其他的位置记为$0$,那么我们看一看$[x, y]$这个区间中最长连续的$1$的个数是不是超过了$k$。这个最长连续$1$的个数可以用线段树维护,维护的方法就是 【Luogu 2572 [SCOI2010]序列操作】这个题的弱化版。    戳这里

那这样我们考虑对于每一个不同的$a_i$建立线段树,发现每一棵线段树有很多结点可以从上一个版本过继下来,所以可持久化之后就变成了一个主席树。

我用的方法是直接把所有的$a_i$离散化,然后用$st$表维护一下每一个区间出现的最大的数和最小的数方便二分。

写得很冗长。

时间复杂度$O(qnlog^{2}n)$。

Code:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

const int N = 1e5 + 5;
const int Lg = 20;
const int inf = 1 << 30;

int n, qn, maxn = 0, a[N];
int v[N], pos[N], len[N], st[2][N][Lg];

struct Item {
    int val, id;
} b[N], in[N];

bool cmp(const Item &x, const Item &y) {
    if(x.val != y.val) return x.val < y.val;
    else return x.id < y.id;
}

inline void read(int &X) {
    X = 0; char ch = 0; int op = 1;
    for(; ch > '9' || ch < '0'; ch = getchar())
        if(ch == '-') op = -1;
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        X = (X << 3) + (X << 1) + ch - 48;
    X *= op;
}

inline int max(int x, int y) {
    return x > y ? x : y;
}

inline int max3(int x, int y, int z) {
    return max(max(x, y), z);
}

inline int min(int x, int y) {
    return x > y ? y : x;
}

inline void chkMax(int &x, int y) {
    if(y > x) x = y;
}

inline void discrete() {
    sort(in + 1, in + 1 + n, cmp);
    for(int cnt = 0, i = 1; i <= n; i++) {
        if(in[i].val != in[i - 1].val) ++cnt;
        a[in[i].id] = cnt;
        v[cnt] = in[i].val;
        chkMax(maxn, cnt);
    }
}

namespace PSegT {
    struct Node {
        int lc, rc, sum, maxSum, lmax, rmax;
        
        inline void init() {
            lc = rc = sum = maxSum = 0;
            lmax = rmax = 0;
        }
        
    } s[N * 80];
    
    int root[N], nodeCnt = 0;
    
    #define mid ((l + r) >> 1)
    #define lc(p) s[p].lc
    #define rc(p) s[p].rc
    #define sum(p) s[p].sum
    #define lmax(p) s[p].lmax
    #define rmax(p) s[p].rmax
    #define maxSum(p) s[p].maxSum
    
    inline void up(int p, int l, int r) {
        if(!p) return;
        maxSum(p) = max3(maxSum(lc(p)), maxSum(rc(p)), lmax(rc(p)) + rmax(lc(p)));
        sum(p) = sum(lc(p)) + sum(rc(p));
        lmax(p) = lmax(lc(p));
        if(sum(lc(p)) == mid - l + 1) chkMax(lmax(p), sum(lc(p)) + lmax(rc(p)));
        rmax(p) = rmax(rc(p));
        if(sum(rc(p)) == r - mid) chkMax(rmax(p), sum(rc(p)) + rmax(lc(p)));
    }
    
    void build(int &p, int l, int r) {
        p = ++nodeCnt;
        if(l == r) {
            sum(p) = lmax(p) = rmax(p) = maxSum(p) = 1;
            return;
        }
        
        build(lc(p), l, mid);
        build(rc(p), mid + 1, r);
        up(p, l, r);
    }
    
    void ins(int &p, int l, int r, int x, int pre) {
        s[p = ++nodeCnt] = s[pre];
        if(l == r) {
            sum(p) = lmax(p) = rmax(p) = maxSum(p) = 0;
            return;
        }
        
        if(x <= mid) ins(lc(p), l, mid, x, lc(pre));
        else ins(rc(p), mid + 1, r, x, rc(pre));
        up(p, l, r);
    }
    
    int go(int p, int l, int r, int x) {
        if(l == x && x == r) return s[p].sum;
        
        if(x <= mid) return go(lc(p), l, mid, x);
        else return go(rc(p), mid + 1, r, x);
    }
    
    int query(int p, int l, int r, int x, int y) {
        if(x <= l && y >= r) return maxSum(p);
        
        int res = 0, lmax = 0, rmax = 0;
        if(x <= mid) lmax = query(lc(p), l, mid, x, y);
        if(y > mid) rmax = query(rc(p), mid + 1, r, x, y);
        if(x <= mid && y > mid) {
            int ln = min(mid - x + 1, rmax(lc(p))), rn = min(y - mid, lmax(rc(p)));
            res = ln + rn;
        }
                
        return max3(lmax, rmax, res); 
    }
    
    #undef mid
    #undef lc
    #undef rc
    #undef sum
    #undef lmax
    #undef rmax
    #undef maxSum
    
} using namespace PSegT;

inline bool chk(int x, int y, int k, int mid) {
    int res = query(root[pos[mid]], 1, n, x, y);
    return res >= k;
}

inline int stQuery(int type, int x, int y) {
    int k = len[y - x + 1];
    if(!type) return max(st[0][x][k], st[0][y - (1 << k) + 1][k]);
    else return min(st[1][x][k], st[1][y - (1 << k) + 1][k]);
}

inline void solve(int x, int y, int k) {
    int ln = stQuery(1, x, y), rn = stQuery(0, x, y), mid, res;
    for(; ln <= rn; ) {
        mid = (ln + rn) / 2;
        if(chk(x, y, k, mid)) res = mid, ln = mid + 1;
        else rn = mid - 1;
    }
    printf("%d\n", v[res]);
}

int main() {
    read(n);
    for(int i = 1; i <= n; i++) {
        read(a[i]);
        in[i].val = a[i], in[i].id = i;
    }
    discrete();
    
/*    for(int i = 1; i <= n; i++)
        printf("%d ", a[i]);
    printf("\n");    */
    
    for(int i = 1; i <= n; i++) {
        b[i].id = i, b[i].val = a[i];
        st[0][i][0] = st[1][i][0] = a[i], len[i] = log2(i);
    }
    
    for(int j = 1; j <= 18; j++)
        for(int i = 1; i + (1 << j) - 1 <= n; i++) {
            st[0][i][j] = max(st[0][i][j - 1], st[0][i + (1 << (j - 1))][j - 1]);
            st[1][i][j] = min(st[1][i][j - 1], st[1][i + (1 << (j - 1))][j - 1]);
        }
    
    sort(b + 1, b + 1 + n, cmp);        
    build(root[1], 1, n);
    int ed[N];
    for(int i = 2; i <= n + 1; i++) {
        ins(root[i], 1, n, b[i - 1].id, root[i - 1]);
        if(b[i].val != b[i - 1].val) ed[b[i - 1].val] = i - 1;
    }    
    
/*    for(int i = 1; i <= maxn; i++)
        printf("%d ", ed[i]);
    printf("\n");    */
        
    for(int i = 1; i <= maxn; i++) pos[i] = ed[i - 1] + 1;
    
/*    for(int i = 1; i <= maxn; i++)
        printf("%d ", v[i]);
    printf("\n");    */
    
/*    for(int i = 1; i <= maxn; i++)
        printf("%d ", pos[i]);
    printf("\n");
    
    for(int i = 1; i <= maxn; i++) {
        printf("%d: ", i);
        for(int j = 1; j <= n; j++)
            printf("%d ", go(root[pos[i]], 1, n, j));
        printf("\n");
    }    */
        
    read(qn);
    for(int x, y, k; qn--; ) {
        read(x), read(y), read(k);
        solve(x, y, k);
    }
    
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/CzxingcHen/p/9664612.html
今日推荐