洛谷 P4587 [FJOI2016]神秘数(主席树,dp)

传送门


解题思路

今晚csp报名网站炸了QAQ,发布新闻者禁三警告
先考虑暴力dp:
O(na)的想必大家都会,但一遍都做不下来。
所以需要换一种dp。
假设求序列[l……r]的答案。
先将其排序,假设到第i-1位时能表示出来的范围为[1..x],则只要判断第i位是否大于x+1即可。
若小于x+1,则范围扩大到x+a[i],向下循环即可。

然后用权值线段树维护区间和,变成主席树维护序列区间。

最后时间复杂度为 \(O(mlog^2a)\)

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int maxn=1e5+5;
const int maxx=1e9+5;
int n,m,cnt,rt[maxn];
long long a[maxn];
struct node{
	int ls,rs;
	long long  value;
}d[maxn*40];
void update(int &x,int last,int l,int r,long long v){
	x=++cnt;
	d[x]=d[last];
	d[x].value+=v;
	if(l==r) return;
	int mid=(l+r)/2;
	if(v<=mid) update(d[x].ls,d[last].ls,l,mid,v);
	else update(d[x].rs,d[last].rs,mid+1,r,v);
}
long long query(int x,int y,int l,int r,long long v){
	if(r<=v){
		return d[y].value-d[x].value;
	}
	int mid=(l+r)/2;
	long long res=0;
	res+=query(d[x].ls,d[y].ls,l,mid,v);
	if(v>mid) res+=query(d[x].rs,d[y].rs,mid+1,r,v);
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		update(rt[i],rt[i-1],1,maxx,a[i]);
	}
	cin>>m;
	for(int i=1;i<=m;i++){
		int l,r;
		cin>>l>>r;
		long long ans=1; 
		while(1){
			long long res=query(rt[l-1],rt[r],1,maxx,ans);
			if(res<ans) break;
			ans=res+1;
		}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2021-10-12 21:41  尹昱钦  阅读(18)  评论(0编辑  收藏  举报