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

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

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

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

时间复杂度的话,因为每次迭代变大 s i s_i si最小也是增加一倍,所以最坏的复杂度也就是 l o g log log级的,所以是可以接受的。

我如果离散化存 a i a_i ai的话,会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;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值