本校 winter training contest 8

HZNU Winter Training Contest 8

D

GYM链接(非group内):https://codeforces.com/gym/102956/problem/N

题意:

给定一个大小为 \(n\)的数组,进行 \(n - 1\)次操作,每次操作:
选择两个相邻的数,将小的数清除,并且把大的数 \(+1\)。若两个数相同可选择保留任意一个。
问经过 \(n - 1\)次操作后(即剩下的唯一一个数),可以是哪个。按递增顺序输出所有可以留到最后的数在原数组的索引。

思路:

对于数组 \(a\) 一开始的区间 \([1,n]\),显然最大的数是一定可以留到最后的。接下来我们可以考虑分治。我们记最大值的索引为 \(id\),显然其把原区间分成了两个区间,及 $[1,id - 1] 和 [id + 1, r] $。那么我们各自考虑这两个区间,我们以左区间为例:对于 \([1,id-1]\),显然其区间内最大值是可以消除整个区间其他所有数的,但是这就意味着它可以剩到最后的吗?显然没有这么简单,我们来考虑这个数的下限是怎么样的,我们记其大小为\(x\),显然其吸收完本区间内所有数以后就会变成 \(x + id - 1\)。所以只要它\(x + id - 2 \geq a_{id}\)就可以吸收所有其他数了。但是真的只有这一个条件吗?
我们来看这样一组样例:
\([1,2,3,5,4]\) 对应到上面就是 \(id = 4\),我们先去解决\([1,3]\),因为在这个区间的最大值 \(3\) 满足\(3 + 3 - 1 = 5 \geq 5\)所以我们继续解决\([1,2]\),我们发现其中的最大值\(2\)也满足\(2 + 2 - 1 = 3 \geq 3\)所以我们也认为其是可以的。可是事实呢?我们发现\(2\)最多只能吸收掉左边的 \(1\) 和右边的 \(3\) 此时其值为 \(4\) ,是无法吸收接下来的 \(5\)的。所以我们发现一个数在某个区间的下限不是这么简单的。我们再来审视一下这个下限,上例中的 \(2\) 会计算错误的原因就是我们只考虑了它在吸收当前区间后能否继续吸收上一个区间,但是显然如果一个数要留到最后,它是要可以吸收所有的区间的,换句话说其实就是:在求出整个区间的最大值以后,分治左右两边的时候,每一边的所有下限对是有效的,再换言之,对于区间\([l, r]\) 其下限是 \(x\) 那么在下个分治区间的最大值 \(a_{id}\) 要满足:
左区间 :\(a_{id} \geq max(x, a_{id} - id + l + 1)\)
右区间 :\(a_{id} \geq max(x, a_{id} - r + id + 1)\)
那么我们只剩下最后一个简单问题了: 如何在 \(O(log_n)\) 的时间内求出一个区间的最大值的位置。这显然只要用线段树简单维护一下就好了。

代码:

#include<bits/stdc++.h>
#define endl '\n'
#define iloveds std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define all(a) a.begin(),a.end()

using namespace std;
typedef long long ll;
const int N = 1e6 + 10;

int n, a[N];
bool f[N];

struct node{
    int val;
}seg[N << 2];
int gao(int l, int r){
    if(a[l] >= a[r]) return l;
    return r;
}

void build(int id, int l, int r){
    if(l == r){
        seg[id].val = l;
    }else{
        int mid = (l + r) >> 1;
        build(id << 1, l, mid);
        build(id << 1 | 1, mid + 1, r);
        seg[id].val = gao(seg[id << 1].val, seg[id << 1 | 1].val);
    }
}

int query(int id, int l, int r, int ql, int qr){
    if(l == ql && r == qr){
        return seg[id].val;
    }else{
        int mid = (l + r) >> 1;
        if(qr <= mid){
            return query(id << 1, l, mid, ql, qr);
        }else if(ql > mid){
            return query(id << 1 | 1, mid + 1, r, ql, qr);
        }else{
            return gao(query(id << 1, l, mid, ql, mid), query(id << 1 | 1, mid + 1, r, mid + 1, qr));
        }
    }
}

void dfs(int l, int r, int x){
    if(l > r) return;
    if(l == r) {
        if(a[l] >= x) f[l] = 1;
        return;
    }
    int tmp = query(1, 1, n, l, r);
    if(a[tmp] < x) return;
    f[tmp] = 1;
    dfs(l, tmp - 1, max(x, a[tmp] - tmp + l + 1));
    dfs(tmp + 1, r, max(x, a[tmp] - r + tmp + 1));
}

int main(){
    iloveds;
    cin >> n;
    for(int i = 1; i <= n ; i ++) cin >> a[i];
    build(1, 1, n);
    dfs(1, n, 0);
    int ans = 0;
    for(int i = 1; i <= n ; i ++){
        if(f[i]) ans ++;
    }
    cout << ans << endl;
    for(int i = 1; i <= n ; i ++){
        if(f[i]) cout << i << " ";
    }
	return 0;
}
posted @ 2022-05-10 21:30  _etilletas  阅读(47)  评论(0编辑  收藏  举报