2020-2021 Winter Petrozavodsk Camp, Belarusian SU Contest (XXI Open Cup, Grand Prix of Belarus)-N. Best Solution Unknown

题意:有n个能力值为Ai的人排成一个序列,一次选择相邻俩个人比赛。比赛后能力值大的人(积累经验)能力值+1,能力小的人被踢出。俩人能力值相等时随机选中一个认为其能力值大。最终剩下一个人时,这个人获得胜利。求所有有可能获得胜利的人的原能力值下标(升序排序输出)。

思路:我们很快注意到,{能力值最大的人(设maxn)一定会获胜(可能有多个最大)。我们随便选一个,那么能力值序列就被分成了左右俩部分}。获取区间最大值可用线段树以O(logn)完成。考虑引入二分思想,即继续求左右俩部分谁可能胜利。假设我们在调用求解左右俩部分时已经获得了左右俩部分胜利的人员名单,我们发现假设左部分胜利者的最终能力值<maxn,他不会在本层获得胜利,也就不会在最终答案中。所以这些胜利者的最终能力值必须要>=maxn才能在这一层胜利。我们发现一个区间[l,r]内胜利者的能力增加了r-l个单位,也就是说胜利者的初始能力值需要>=maxn-r+l,>=max-r+l这个条件可以通过函数参数下传,在求解左右子任务前检查,递归构建完成。

代码://用到了模板式线段树以及std::tuple等,请使用C++17标准编译(逃

#include <bits/stdc++.h>
using INT=int;
#define pb push_back
#define eb emplace_back
#define all(a) (a).begin(),(a).end()
using namespace std;
using ll=long long;
using vi=vector<int>;
using vll=vector<ll>;
template<class T>
void _read(istream& is,T &a){is>>a;}
void _read(istream& is,ll &a){
    a=0;bool f=false;char c;
    while (!isdigit(c=is.get())&&is.good()) f=c=='-';
    if (!is.good()) return ;
    do a=(a<<3)+(a<<1)+c-'0'; while (isdigit(c=is.get()));
    if (f) a=-a;
}
 
void read(){}
template<class T,class ...Args>
void read(T& a,Args& ...args){_read(cin,a);read(args...);}
template<class T>
void reada(vector<T>& v,size_t n){
	v.reserve(n);
	for (size_t i=0;i<n;i++){
		T a;cin>>a;
		v.emplace_back(move(a));
	}
}
template<class T,int count,class IT=ll>
struct SegTree{
    SegTree<T,count-1> ch[2];
    constexpr static IT mid=1ll<<(count-1);
    constexpr static IT tot=1ll<<count;
    IT maxn,maxni;
    SegTree(){maxn=maxni=0;}
    void pushup(){
    	if (ch[0].maxn<ch[1].maxn){
    		maxn=ch[1].maxn;
    		maxni=mid+ch[1].maxni;
    	}else{
    		maxn=ch[0].maxn;
    		maxni=ch[0].maxni;
    	}
    }
    void set(const IT i,const T &nadd){
        if (i<=mid) ch[0].set(i,nadd);
        if (i>mid) ch[1].set(i-mid,nadd);
        pushup();
    }
    tuple<ll,ll> getmax(const IT &l,const IT &r){
        if (l<=1&&tot<=r) return {maxn,maxni};
        tuple<ll,ll> ans={-1,0};
        if (l<=mid){
        	auto a=ch[0].getmax(l,r);
        	if (get<0>(a)>get<0>(ans))
        		ans=a;
        }
        if (r>mid){
        	auto a=ch[1].getmax(l-mid,r-mid);
        	if (get<0>(a)>get<0>(ans)){    		
        		ans=a;
	        	get<1>(ans)+=mid;
        	}
        }
        return ans;
    }
};
template<class T,class IT>
struct SegTree<T,0,IT>{
    IT maxn,maxni;
    SegTree(){maxn=0;maxni=1;}
    tuple<ll,ll> getmax(const IT &l,const IT &r){return {maxn,maxni};}
    void set(const IT i,const T &nadd){maxn=nadd;}
};
 
struct solution{
	int n;
	SegTree<ll,20> sg;
	void scan(){
		read(n);
		for (int i=1;i<=n;i++){
			ll x;
			read(x);
			sg.set(i,x);
		}
	}
	vi ans;
	void f(int l,int r,ll maxn,ll maxni,ll limit){
		if (maxn<=limit) return;
		ans.pb(maxni);
		if (maxni-1>=l){
			auto [lmaxn,lmaxni]=sg.getmax(l,maxni-1);
			if (lmaxn+maxni-l-1>=maxn){
				f(l,maxni-1,lmaxn,lmaxni,max(limit,maxn-(maxni-l)));
			}
		}
		if (maxni+1<=r){
			auto [rmaxn,rmaxni]=sg.getmax(maxni+1,r);
			if (rmaxn+r-maxni-1>=maxn){
				f(maxni+1,r,rmaxn,rmaxni,max(limit,maxn-(r-maxni)));
			}
		}
	}
	int solve(){
		auto k=sg.getmax(1,n);
		f(1,n,get<0>(k),get<1>(k),0);
		cout << ans.size() << endl;
		sort(all(ans));
		for (auto ansi:ans)
			cout << ansi << " ";
		return 0;
	} 
};
	
 
INT main(){
	cin.tie(0);
	ios::sync_with_stdio(false);
	auto a=unique_ptr<solution>(new solution());
	a->scan();
	a->solve();
	return 0;
}