Loading

P4587 [FJOI2016]神秘数-题解

P4587 [FJOI2016]神秘数-题解

思路

我们考虑将询问的区间 \([l,r]\) 内的所有数先从小到大排序,然后从左到右扫一遍

我们假设当前扫到了 \(a_i\) 且用 \(a_l\sim a_{i-1}\) 所能表示的值域为 \([1,x]\)

  • \(a_i>x+1\),则我们无论如何都表示不出 \(x+1\),直接输出答案

  • 否则 \(a_i\le x+1\),则此时我们可以表示的值域就变为 \([1,x+a_i]\)

这样的朴素暴力时间复杂度是 \(\text O(nm\log n)\) 的,需要优化

若当前的答案为 \(ans\),且小于等于 \(ans\) 的数的和为 \(res\),则显然 \(ans\) 需要更新为 \(res+1\)

否则答案就是 \(ans\)

那我们所需要的就是维护某段区间内的某段值域内所有数的和

显然用主席树维护

时间复杂度为 \(\text O((n+m)\log n\log (\sum a_i))\)

code

#include<bits/stdc++.h>
using namespace std;

#define doge_is_god 1
const int N=1e5+5;
const int inf=1e9;

inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}

int n,m;
int a[N],rt[N];

namespace CT{
	struct node{
		int v,l,r;
	}t[N*31];
	int cnt;
	inline void insert(int &p,int pre,int l,int r,int x,int k){
		p=++cnt;
		t[p]=t[pre];
		t[p].v+=k;
		if(l==r) return;
		int mid=l+r>>1;
		if(x<=mid) insert(t[p].l,t[pre].l,l,mid,x,k);
		else insert(t[p].r,t[pre].r,mid+1,r,x,k);
	}
	inline int query(int q,int p,int l,int r,int L,int R){
		if(L<=l&&r<=R) return t[p].v-t[q].v;
		int mid=l+r>>1,res=0;
		if(L<=mid) res+=query(t[q].l,t[p].l,l,mid,L,R);
		if(mid<R) res+=query(t[q].r,t[p].r,mid+1,r,L,R);
		return res;
	}
}

signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		a[i]=read();
		CT::insert(rt[i],rt[i-1],1,inf,a[i],a[i]);
	}
	m=read();
	while(m--){
		int l=read(),r=read(),ans=1;
		while(doge_is_god){
			int res=CT::query(rt[l-1],rt[r],1,inf,1,ans);
			if(res>=ans) ans=res+1;
			else break;
		}
		printf("%d\n",ans);
	}
}
posted @ 2022-07-28 20:37  Into_qwq  阅读(114)  评论(0编辑  收藏  举报