[FJOI2016]神秘数
Description:
给定一个数列,每次询问不能被区间\([L,R]\)所有数的任何子集表示的最小正整数
Hint:
\(n \le 10^5 ,a_i \le 10^9\)
Solution:
很好的思维题
我们先令\(Ans=1\),使区间中的数从小到大依次加入
考虑已知能表示的值域为\([1,x]\),现在的最小答案\(Ans=x+1\),重新考虑小于\(Ans\)的数对\(ans\)造成的影响
显然最多表示出\([1,\sum a_i(a_i<Ans)]\) (因为之前的为\([1,\sum a_i(a[i]<Ans_{pre})]\),加进来的数一定都能和之前的数表示出这一段)
如果\(\sum a_i(a_i<Ans)\) \(>= Ans\) 则 ans =\(\sum a_i(a_i<Ans)+1\)
否则直接输出答案
所以可以用主席树维护,复杂度\(O(mlogn* loga_i)\)
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mxn=1e5+5,inf=1e9;
ll n,m,cnt,tot,hd[mxn],a[mxn];
ll rt[mxn<<5],ls[mxn<<5],rs[mxn<<5];
ll sum[mxn<<5];
inline ll read() {
char c=getchar(); ll x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}
struct ed {
ll to,nxt;
}t[mxn<<1];
inline void add(ll u,ll v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
void update(ll las,ll &p,ll l,ll r,ll pos,ll val) {
if(!p) p=++tot; sum[p]=sum[las]+val;
if(l==r) return ; ll mid=(l+r)>>1;
if(pos<=mid) update(ls[las],ls[p],l,mid,pos,val),rs[p]=rs[las];
else update(rs[las],rs[p],mid+1,r,pos,val),ls[p]=ls[las];
}
ll query(ll las,ll p,ll l,ll r,ll pos) {
if(l==r) return sum[p]-sum[las]; ll mid=(l+r)>>1;
if(pos<=mid) return query(ls[las],ls[p],l,mid,pos);
else return sum[ls[p]]-sum[ls[las]]+query(rs[las],rs[p],mid+1,r,pos);
}
int main()
{
n=read(); ll l,r;
for(ll i=1;i<=n;++i)
a[i]=read(),update(rt[i-1],rt[i],1,inf,a[i],a[i]);
m=read(); sort(a+1,a+n+1);
for(ll i=1;i<=m;++i) {
l=read(),r=read();
ll ans=1;
while(1) {
ll pos=upper_bound(a+1,a+n+1,ans)-a-1; ll x;
if((x=query(rt[l-1],rt[r],1,inf,ans))>=ans) ans=x+1;
else break ;
}
printf("%lld\n",ans);
}
return 0;
}