洛谷 P4587 [FJOI2016]神祕數(主席樹,dp)
阿新 • • 發佈:2021-10-13
傳送門
解題思路
今晚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; }