神秘题目3

神秘题目

题目描述

给定整数序列 \(a_1,...,a_n\)

\(m\) 次询问,每次给定 \(L,R\) 求一个最小的非负整数 \(x\)

满足对于任意的 \(b_i\in\{0,1\}\) ,都满足 \(x≠\sum\limits_{i=L}^R{a_i\times b_i}\)

输入格式

第一行 2 个整数 \(n\),\(m\)

接下来 1 行 \(n\) 个整数表示 \(a_i\)

接下来 \(m\) 行,每行第一个整数 \(opt\)

  • \(opt=1\) ,接下来 2 个整数 \(k,y\) 表示令 \(a_k=y\)
  • \(opt=2\) ,接下来 2 个整数 \(L,R\) 表示一次查询操作

输出格式

若干行每行一个整数,对应一次查询操作的答案。

样例输入

5 5
3 4 2 1 9
2 1 5
1 1 2
2 1 3
1 2 8
2 2 4

样例输出

20
1
4

数据范围与约定

对于 \(10\%\) 的数据 \(n,m\le 10\)

对于 \(30\%\) 的数据 \(n,m\le 2000\)\(x\le2000\)

另有 \(20\%\) 的数据 \(L=1,R=n,m=3\)

对于 \(100\%\) 的数据 \(n,m\le 4\times 10^4 ,1\le a_i\le10^9\)

时间限制:\(1s\)

空间限制:\(256MB\)

提示: \(4\times 10^4=200^2\) , \(n\sqrt{n}\)

题解

10分做法

爆搜啥的都行

30分做法

注意到答案 \(x\) 不超过 \(2000\),可以考虑背包

LL ask(int L,int R)
{
	memset(vis,0,sizeof vis);
	vis[0]=1;
	for(int i=L;i<=R;i++)
	for(int j=2000;j>=a[i];j--)
		vis[j]|=vis[j-a[i]];
	for(int i=1;i<=2000;i++)
	if(!vis[i])return i;
	return 0;
}

另外20分

其实就是送的,\(m\) 只有 \(3\)

每次询问时,将 \(a_i\) 从小到大排序。设一个累加器 \(sum=0\),从小到大考虑每一个值,如果 \(a_i\le sum+1\) ,那么将 \(sum\) 加上 \(a_i\) 。否则 \(sum+1\) 就是答案。

原理:\(sum\) 其实代表 \([0,sum]\) 的值都可以拼出来,如果 \(a_i\le sum+1\) 那么 \([0,sum+a_i]\) 也都可以拼出来。

这一档分对正解有提示作用。

满分做法

上一档分的瓶颈在于排序和累加,这两项其实不可以通过分块优化(或者是我不会)

一个显而易见的结论

如果一组 $a_ i $ 使 \([0,2^k]\) 都可以拼出来并且 \(\sum a_i \le 2^{k+1}\) ,只要再多一个 \(p\in [2^k+1,2^{k+1}]\)\(p\le \sum a_i +1\) ,那么 \([0,2^{k+1}]\) 都可以拼出来。

有了这个结论相信大家都会做了。可以用线段树维护 $a_l .. a_r $ 中所有大小在 \([2^k,2^{k+1}]\) 范围内的 $a_i $ 和,和最小值。

那么扫描每一段 \([2^k,2^{k+1}]\) ,就行了。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int inf=1<<30;
int n,m,a[40010],mi[40]={0,1},Lbel,Nbel;
LL sum[31],ans;int minn[31];
struct SegmentTree
{int l,r,min[31];LL data[31];}st[160010];
inline int read()
{
	int x=0;bool w=0;char ch=0;
	while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return w?-x:x;
}
inline int getbel(int x)
{
	if(x==1)return 1;
	int temp=1,l=1,r=30,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(x>=mi[mid])temp=mid,l=mid+1;
		else r=mid-1;
	}
	return temp;
}
void build(int p,int l,int r)
{
	st[p].l=l;st[p].r=r;
	if(l==r){
		int Bel=getbel(a[l]);
		for(int i=1;i<=30;i++)st[p].min[i]=inf;
		st[p].data[Bel]=st[p].min[Bel]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	for(int i=1;i<=30;i++){
		st[p].data[i]=st[p<<1].data[i]+st[p<<1|1].data[i];
		st[p].min[i]=min(st[p<<1].min[i],st[p<<1|1].min[i]);
	}
}
void change(int p,int pos,int k)
{
	if(st[p].l==st[p].r){
		st[p].data[Lbel]=0;st[p].min[Lbel]=inf;
		st[p].data[Nbel]=st[p].min[Nbel]=k;
		return;
	}
	int mid=(st[p].l+st[p].r)>>1;
	if(pos<=mid)change(p<<1,pos,k);
	else change(p<<1|1,pos,k);
	st[p].data[Lbel]=st[p<<1].data[Lbel]+st[p<<1|1].data[Lbel];
	st[p].min[Lbel]=min(st[p<<1].min[Lbel],st[p<<1|1].min[Lbel]);
	st[p].data[Nbel]=st[p<<1].data[Nbel]+st[p<<1|1].data[Nbel];
	st[p].min[Nbel]=min(st[p<<1].min[Nbel],st[p<<1|1].min[Nbel]);
}
void calc(int p,int l,int r)
{
	if(l<=st[p].l&&st[p].r<=r){
		for(int i=1;i<=30;i++){
			minn[i]=min(minn[i],st[p].min[i]);
			sum[i]+=st[p].data[i];
		}
		return;
	}
	int mid=(st[p].l+st[p].r)>>1;
	if(l<=mid)calc(p<<1,l,r);
	if(mid<r)calc(p<<1|1,l,r);
}
LL ask(int l,int r)
{
	memset(minn,0x3f,sizeof minn);
	memset(sum,0,sizeof sum);
	calc(1,l,r);
	ans=sum[1];
	for(int i=2;i<=30;i++)
	if(ans+1>=minn[i])ans+=sum[i];
	return ans+1;
}
int main()
{
	freopen("min.in","r",stdin);
	freopen("min.out","w",stdout);
	for(int i=2;i<=30;i++)mi[i]=mi[i-1]*2;
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	build(1,1,n);
	while(m--){
		int opt=read(),l=read(),r=read();
		if(opt&1){
			Lbel=getbel(a[l]);Nbel=getbel(a[l]=r);
			change(1,l,r);
		}else printf("%lld\n",ask(l,r));
	}
}
posted @ 2021-02-25 20:25  zYzYzYzYz  阅读(90)  评论(1编辑  收藏  举报