神秘题目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));
}
}