[FJOI 2016]bzoj 4408 神秘数 - 线段树

题目大意:给你一列数,多次询问用一个区间的数字形成一个可重集合,最小的不能被表示为其一个子集的数字是多少。
题解:考虑给你一个可重集合你怎么算:从小到大排序,假设用前x个数字不能表示的最小都数字是ans,那么如果a[x+1]>ans,则ans就是答案,否则ans+=a[++x]。这个过程显然可以线段树每次区区间最小值,加上,然后把这个最小值设为INF,但是复杂度是不对的,例如全是1。但是发现这个过程显然可以优化:若当前答案是ans,已经加入了a[1…x],而a[(x+1)…y]<=ans,那么可以一口气把a[(x+1)…y]加到ans上(此时ans=\sum_{i=1}^y a[i])。这样做好像复杂度还是不靠谱?其实是对的,考虑一个数字x,再第i轮(此时答案记做ans[i])没有被加入,而第i+1轮被加入了,意味着x>ans[i],而从第i轮到第i+2轮,ans至少翻了一倍。因此用主席树维护上述过程,复杂度两个log。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define gc getchar()
#define N 100010
#define pb push_back
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
inline int inn()
{
	int x,ch;while((ch=gc)<'0'||ch>'9');
	x=ch^'0';while((ch=gc)>='0'&&ch<='9')
		x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct segment{
	int s;segment *ch[2];
}*T[N];int m,a[N];vector<int> v;
inline int getid(int x) { return lower_bound(v.begin(),v.end(),x)-v.begin()+1; }
int build(segment* &rt,int l,int r)
{
	rt=new segment,rt->s=0;if(l==r) return 0;int mid=(l+r)>>1;
	return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),0;
}
int update(segment* &x,segment* &y,int l,int r,int p,int v)
{
	x=new segment,x->s=y->s+v,x->ch[0]=y->ch[0],x->ch[1]=y->ch[1];
	if(l==r) return 0;int mid=(l+r)>>1;
	if(p<=mid) update(x->ch[0],y->ch[0],l,mid,p,v);
	if(mid<p) update(x->ch[1],y->ch[1],mid+1,r,p,v);
	return 0;
}
int query(segment* &rt,int l,int r,int s,int t)
{
	if(s<=l&&r<=t) return rt->s;int mid=(l+r)>>1,ans=0;
	if(s<=mid) ans+=query(rt->ch[0],l,mid,s,t);
	if(mid<t) ans+=query(rt->ch[1],mid+1,r,s,t);
	return ans;
}
int query(int l,int r,int p)
{
	int t=getid(p);if(v[t-1]>p) t--;if(!t) return 0;
	return query(T[r],1,m,1,t)-query(T[l-1],1,m,1,t);
}
int main()
{
	int n=inn();
	for(int i=1;i<=n;i++) a[i]=inn(),v.pb(a[i]);
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	m=(int)v.size();build(T[0],1,m);
	for(int i=1;i<=n;i++)
		update(T[i],T[i-1],1,m,getid(a[i]),a[i]);
	for(int q=inn();q;q--)
	{
		int l=inn(),r=inn(),ans=0,t;
		while(ans<(t=query(l,r,ans+1))) ans=t;
		printf("%d\n",ans+1);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mys_C_K/article/details/82936419