2020ICPC 昆明 M.Stone Games(思维+可持久化线段树)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020ICPC 昆明 M.Stone Games(思维+可持久化线段树)相关的知识,希望对你有一定的参考价值。

LINK

题意

给出长度为 n n n的数组 a a a q q q次询问,每次给出区间 [ l , r ] [l,r] [l,r]

你可以用区间 [ l , r ] [l,r] [l,r]的任意子集来凑数,问最小的不能凑到的数是多少


其实有点像 01 01 01背包问题,但显然不符合时间复杂度

考虑区间 [ l , r ] [l,r] [l,r]内没有数字 1 1 1,那么答案就是 1 1 1

否则设我们拥有 x x x个数字 1 1 1,毫无疑问可以凑出 [ 1 , x ] [1,x] [1,x]

k k k是继数字 1 1 1后最小的数字,若存在 k > x + 1 k>x+1 k>x+1

显然 x + 1 x+1 x+1就是答案,因为这不可能被凑出来,之后的数组也比这个大

若存在 k < = x + 1 k<=x+1 k<=x+1,那么 [ 1 , x + k ] [1,x+k] [1,x+k]都是可以被凑出来的…


分析到这里,做法已经差不多出来了

当我们能凑成 [ 1 , x ] [1,x] [1,x]时,就查询区间 [ l , r ] [l,r] [l,r]内所有小于等于 x + 1 x+1 x+1的数和 s u m sum sum(这个 s u m sum sum不包括凑出 [ 1 , x ] [1,x] [1,x]的那些数)

如果 s u m = = 0 sum==0 sum==0,显然 x + 1 x+1 x+1就是答案

否则令 x = x + s u m x=x+sum x=x+sum,如此迭代下去

容易发现迭代次数是 l o g log log级别,区间查询使用可持久化线段树实现

注意

①.在主席树上查询时注意查的不能超过值域,不然会卡死

②.开long long

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 1e9;
const int maxn = 1e6+10;
const int N = 4e7+10;
int n,q,a[maxn];
ll ans[2];
int root[maxn],ls[N],rs[N],id;
ll sum[N];
void update(int &rt,int pre,int l,int r,int val)
{
	rt = ++id;
	ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre]+val;
	if( l==r )	return;
	int mid = l+r>>1;
	if( val<=mid )	update( ls[rt],ls[pre],l,mid,val);
	else	update( rs[rt],rs[pre],mid+1,r,val );
}
ll ask(int rt,int pre,int l,int r,int L,int R)//查询[L,R]有多少
{
	if( l>=L && r<=R )	return sum[rt]-sum[pre];
	if( l>R || r<L )	return 0ll;	
	int mid = l+r>>1;
	return ask( ls[rt],ls[pre],l,mid,L,R )+ask( rs[rt],rs[pre],mid+1,r,L,R );
} 
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++)	scanf("%d",&a[i] );
	for(int i=1;i<=n;i++)
		update( root[i],root[i-1],0,inf,a[i] );
	int t = 1;
	while( q-- )
	{
		int lp,rp,l,r;
		scanf("%d%d",&lp,&rp );
		l = min( (1ll*lp+ans[t^1])%n+1,(1ll*rp+ans[t^1])%n+1 );
		r = max( (1ll*lp+ans[t^1])%n+1,(1ll*rp+ans[t^1])%n+1 );	
		ll x = ask( root[r],root[l-1],0,inf,1,1 );//查询1的个数 
		ll las = x;
		while( true )
		{
			ll he = ask( root[r],root[l-1],0,inf,1,min(x+1,1ll*inf) )-las;//小于等于x+1的数字都可以加进来,当然需要除掉之前用的数las 
			if( he==0 )	break;
			x = x+he, las = x;
		}
		ans[t] = x+1;
		printf("%lld\\n",ans[t] );
		t ^= 1;
	}
}

以上是关于2020ICPC 昆明 M.Stone Games(思维+可持久化线段树)的主要内容,如果未能解决你的问题,请参考以下文章

2020ICPC昆明 J.Parallel Sort(思维,规律)

2020ICPC 昆明热身赛 C.Statues(小思维)

2020ICPC昆明 C.Cities(区间dp)

2020ICPC昆明 L.Simone and graph coloring(思维+nlog(n)的最长下降子序列)

ACM周赛&ICPC昆明资格赛

ICPC20昆明M——动态开点权值线段树(主席树)