2020牛客多校第四场 Eliminate++(线段树优化贪心)

链接:https://ac.nowcoder.com/acm/contest/5669/E
来源:牛客网

题目描述
ZYB likes to create puzzles for himself and then solve them.

There are \ N N (\ N N is odd) distinct integers written in a line on the blackboard, and you decide to erase those numbers from the blackboard.
Since you have just learned the concepts of median during the lecture, you invented the following erase-operation for three integers: wipe off the largest number and the smallest number from the blackboard, so that only the median of the three numbers remains. You decide to repeat the following process: choose three consecutive integers on the blackboard and apply erase-operation on them. After this operation, the number of integers on the blackboard will decrease by 2. Eventually, there will be only one integer left after this process is repeated N − 1 2 \frac{N-1}{2} 2N1 times.

ZYB comes up with an interesting question: which integers may survive until the end?


2019香港区域赛出过这题的弱化版

题意:
n个不相同数的数列,每次可以选连续3个数并删掉最大值最小值。
n是奇数,求每个数字最后是否能剩下。

思路:
如果数据范围1e3的话,那么可以 n 2 n^2 n2的写。枚举每个数,把比他大的设置成1,比他小的设置成0。

那么对于这个数左边的段和右边的段单独考虑,再对于每个段进行遍历,遇到1就加一,遇到0就减一,维护前缀和的大小,前缀和为负数就重置为0,如果前缀和的数目等于3,那么说明1的数目可以比0多减少2,这样进行下去。因为只要这一段1的数目比0多3,那么最后就可以删成111的形式,1的数目也就相对减少2了,也就是有效删除

这样可以算出最多减少多少个1和最多减少多少个0,最后只要1的数目等于0的数目就可以全部删掉了。


这个过程可以用线段树维护,官方题解没有看懂,参考的是这位大佬的博客

维护的是 t [ i ] . n u m [ j ] t[i].num[j] t[i].num[j], 代表 i i i为根节点的子树左边插入 j j j个1后,最多能进行多少次有效删除(数目相对减少2)。
t [ i ] . r e s [ j ] t[i].res[j] t[i].res[j]这个子树左边插入 j j j个1后,进行完所有有效删除后,右边最多剩下多少个1。

这就能算出来最多删除多少个1了。

这和贪心思路等价,意思就是对于每个区间进行完有效删除,右边最多还能剩下多少个1给后面的区间。这就能算出1和0的数目是否相同了。(大于 ( 1 + n ) / 2 (1+n)/2 (1+n)/2的数和小于 ( 1 + n ) / 2 (1+n)/2 (1+n)/2的数对称,可以把0当做1,1当做0)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 1e6 + 7;

int a[maxn],pos[maxn],ok[maxn];

struct Tree {
    
    
    int l,r;
    int num[4],res[4];
}t[maxn << 2];

void pushup(int i) {
    
    
    for(int j = 0;j < 3;j++) {
    
    
        t[i].num[j] = t[i * 2].num[j] + t[i * 2 + 1].num[t[i * 2].res[j]];
        t[i].res[j] = t[i * 2 + 1].res[t[i * 2].res[j]];
    }
}

void build(int i,int l,int r) {
    
    
    t[i].l = l;t[i].r = r;
    if(l == r) {
    
    
        for(int j = 0;j < 3;j++) {
    
    
            t[i].num[j] = j >= 2;
            t[i].res[j] = j % 2 + 1;
        }
        return;
    }
    int m = (l + r) >> 1;
    build(i * 2,l,m);
    build(i * 2 + 1,m + 1,r);
    pushup(i);
}

void update(int i,int x) {
    
     //把x这个点更新为0
    if(t[i].l == t[i].r) {
    
    
        for(int j = 0;j < 3;j++) {
    
    
            t[i].num[j] = t[i].res[j] = 0;
        }
        t[i].res[2] = 1;
        return;
    }
    int m = (t[i].l + t[i].r) >> 1;
    if(x <= m) {
    
    
        update(i * 2,x);
    }
    if(x > m) {
    
    
        update(i * 2 + 1,x);
    }
    pushup(i);
}

int query(int i,int x,int y,int &res) {
    
    
    if(x <= t[i].l && t[i].r <= y) {
    
    
        int tmp = res;
        res = t[i].res[res];
        return t[i].num[tmp];
    }
    int m = (t[i].l + t[i].r) >> 1;
    int ans = 0;
    if(x <= m) {
    
    
        ans += query(i * 2,x,y,res);
    }
    if(y > m) {
    
    
        ans += query(i * 2 + 1,x,y,res);
    }
    return ans;
}

int main() {
    
    
    int T;scanf("%d",&T);
    while(T--) {
    
    
        int n;scanf("%d",&n);
        for(int i = 1;i <= n;i++) {
    
    
            scanf("%d",&a[i]);
            pos[a[i]] = i;
        }
        build(1,1,n);
        int mid = (1 + n) / 2;
        for(int i = 1;i < mid;i++) {
    
    
            int l = 1,r = pos[i] - 1;
            update(1,pos[i]);
            int ans = 0;
            int res = 0;
            if(l <= r) {
    
    
                ans += query(1,l,r,res);
            }
            
            l = pos[i] + 1,r = n;
            res = 0;
            if(l <= r) {
    
    
                ans += query(1,l,r,res);
            }
            
            if(ans * 2 + i - 1 >= n - i) {
    
    
                ok[i] = 1;
            } else {
    
    
                ok[i] = 0;
            }
        }
        ok[mid] = 1;
        build(1,1,n);
        for(int i = n;i > mid;i--) {
    
    
            int l = 1,r = pos[i] - 1;
            update(1,pos[i]);
            int ans = 0;
            int res = 0;
            if(l <= r) {
    
    
                ans += query(1,l,r,res);
            }
            
            l = pos[i] + 1,r = n;
            res = 0;
            if(l <= r) {
    
    
                ans += query(1,l,r,res);
            }
            
            if(ans * 2 + n - i >= i - 1) {
    
    
                ok[i] = 1;
            } else {
    
    
                ok[i] = 0;
            }
        }
        for(int i = 1;i <= n;i++) {
    
    
            printf("%d",ok[a[i]]);
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/tomjobs/article/details/109122105