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