1. 程式人生 > 其它 >洛谷 P4587 [FJOI2016]神祕數(主席樹,dp)

洛谷 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;
}