Loading

2020ICPC昆明 M - Stone Games (主席树 + 思维)解决区间Mex问题

题意:
给你一个长度为\(n\)的数组,每次询问给出一个区间\([l,r]\),问你用当前的区间内的任意个数,不能组成的最小的正整数是多少?

思路:
这道题其实需要解决的就是,怎么快速求出来,这个不能表示的数是多少,我们这样考虑,假设把当前给定的区间的内的数\(a_l,a_{l+1},....,a_r\)按照从小到大的顺序排起来,考虑\(s_i\)为这组序列前\(i\)个数的和,我们如果能找到,\(s_i+1 < a_{i+1}\),是不是就代表这个\(s_i+1\)就是我们表示不出来的数呢,因为前面的那些小的数加起来都凑不够,而后面的任意一个数又比它大,所以肯定是凑不出来的。

类似于一种套路吧应该,知道了这个之后,我们能否快速求出来,区间不能表示的数呢。
也就是说假如一个数\(a_{i+1} \leq s_i + 1\),那么我们就可以把它加入到我这个和中,所以对于每个\(s_i + 1\),我们只需要找出来当前这个区间内\(\leq s_i+1\)的所有的数有哪些,然后把他们的和加入到\(s_i\)中即可,因为这些一定合法。
所以我们只需要找到能够快速查询区间内小于等于某个数的所有数的和的这样一种工具即可,主席树即可完成。

时间复杂度的话,因为每次迭代变大\(s_i\)最小也是增加一倍,所以最坏的复杂度也就是\(log\)级的,所以是可以接受的。

我如果离散化存\(a_i\)的话,会MLE,不知道为什么,但因为是主席树查值域,所以不用离散化也可。

#include <bits/stdc++.h>

using namespace std;
#define int long long
#define pb push_back
#define eb emplace_back
#define MP make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define lson rt<<1
#define rson rt<<1|1
#define CLOSE std::ios::sync_with_stdio(false)
#define sz(x) (int)(x).size()
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-6;
const int N = 1e6 + 10;
//知道某个区间内不超过某个数的所有数的和即可
int n,m,root[N * 40],tot;
ll a[N];
struct CT {
	int l,r;
	ll sum;
}tree[N * 40];
void pushup(int now) {
	tree[now].sum = tree[tree[now].l].sum + tree[tree[now].r].sum;
}
void build(int &now,int l,int r) {
	tree[now = ++tot];
	tree[now].l = l,tree[now].r = r;
	if(l == r) {
		tree[now].sum = 0; return ;
	}
	int mid = (l + r) >> 1;
	build(tree[now].l,l,mid); build(tree[now].r,mid+1,r);
	pushup(now);
}
void modify(int &now,int pre,int l,int r,int pos,ll v) {
	tree[now = ++tot] = tree[pre];
	if(l == r) {
		tree[now].sum += v; return ;
	}
	int mid = (l + r) >> 1;
	if(pos <= mid) modify(tree[now].l,tree[pre].l,l,mid,pos,v);
	else modify(tree[now].r,tree[pre].r,mid+1,r,pos,v);
	pushup(now);
}
ll query(int now,int pre,int l,int r,int L,int R) {
	if(l >= L && r <= R) return tree[now].sum - tree[pre].sum;
	int mid = (l + r) >> 1;
	ll ans = 0;
	if(L <= mid) ans += query(tree[now].l,tree[pre].l,l,mid,L,R);
	if(R > mid) ans += query(tree[now].r,tree[pre].r,mid+1,r,L,R);
	return ans; 
}
int lsh[N],cnt;
int getid(ll x) {
	return lower_bound(lsh + 1,lsh + 1 + cnt,x) - lsh;
}
// std::map<ll,bool>vis;

signed main() {
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;i ++) {
		scanf("%lld",&a[i]);
	}
	// sort(lsh + 1,lsh + 1 + n);
	// cnt = unique(lsh + 1,lsh + 1 + cnt) - lsh - 1;
	// build(root[0],1,(int)1e9);
	// cout << "---------------\n";
	for(int i = 1;i <= n;i ++) {
		// int p = getid(a[i]);
		modify(root[i],root[i-1],1,(int)1e9,a[i],a[i]);
	}
	// sort(a + 1,a + 1 + n);
	int l,r;
	ll ans = 0;
	for(int _ = 1;_ <= m;_ ++) {
		scanf("%lld%lld",&l,&r);
		l = (l + ans) % n + 1;
		r = (r + ans) % n + 1;
		if(l > r) swap(l,r);
		// cout << l << ' ' << r << '\n';
		ll f = 0,ff;
		while(true) {
			ll ff = query(root[r],root[l-1],1,(int)1e9,1,f + 1);
			// cout << ff << "***\n";
			if(ff == f) break;
			f = ff;
			// cout << f << "***\n";
		}
		ans = f + 1;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-06-02 14:17  x7x7g7c7  阅读(209)  评论(0)    收藏  举报