神秘题目
题目描述
给定整数序列 (a_1,...,a_n) 。
(m) 次询问,每次给定 (L,R) 求一个最小的非负整数 (x)
满足对于任意的 (b_iin{0,1}) ,都满足 (x≠sumlimits_{i=L}^R{a_i imes 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,mle 10)
对于 (30\%) 的数据 (n,mle 2000),(xle2000)
另有 (20\%) 的数据 (L=1,R=n,m=3)
对于 (100\%) 的数据 (n,mle 4 imes 10^4 ,1le a_ile10^9)
时间限制:(1s)
空间限制:(256MB)
提示: (4 imes 10^4=200^2) , (nsqrt{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_ile sum+1) ,那么将 (sum) 加上 (a_i) 。否则 (sum+1) 就是答案。
原理:(sum) 其实代表 ([0,sum]) 的值都可以拼出来,如果 (a_ile sum+1) 那么 ([0,sum+a_i]) 也都可以拼出来。
这一档分对正解有提示作用。
满分做法
上一档分的瓶颈在于排序和累加,这两项其实不可以通过分块优化(或者是我不会)。
一个显而易见的结论
如果一组 $a_ i $ 使 ([0,2^k]) 都可以拼出来并且 (sum a_i le 2^{k+1}) ,只要再多一个 (pin [2^k+1,2^{k+1}]) 且 (ple 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
",ask(l,r));
}
}