洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治優化DP
阿新 • • 發佈:2020-09-08
洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治優化DP
題目描述
佳媛姐姐過生日的時候,她的小夥伴從某寶上買了一個有趣的玩具送給他。
玩具上有一個數列,數列中某些項的值可能會變化,但同一個時刻最多隻有一個值發生變化。現在佳媛姐姐已經研究出了所有變化的可能性,她想請教你,能否選出一個子序列,使得在任意一種變化中,這個子序列都是不降的?請你告訴她這個子序列的最長長度即可。
輸入格式
輸入的第一行有兩個正整數 \(n,m\),分別表示序列的長度和變化的個數。
接下來一行有 \(n\) 個整數,表示這個數列原始的狀態。
接下來 \(m\) 行,每行有 \(2\) 個整數 \(x,y\)
輸出格式
輸出一個整數,表示對應的答案。
輸入輸出樣例
輸入 #1
3 4
1 2 3
1 2
2 3
2 1
3 4
輸出 #1
3
說明/提示
注意:每種變化最多隻有一個值發生變化。
在樣例輸入中,所有的變化是:
1 2 3
2 2 3
1 3 3
1 1 3
1 2 4
選擇子序列為原序列,即在任意一種變化中均為不降子序列。
對於 \(20\%\) 資料,所有數均為正整數,且小於等於 \(300\)。
對於 \(50\%\) 資料,所有數字均為正整數,且小於等於 \(3000\)。
對於 \(100\%\) 資料,所有數字均為正整數,且小於等於 \(10^5\)
分析
我們設\(min[i]\)為處在位置\(i\)上的數變化得到的最小值,\(max[i]\)為處在位置\(i\)上的數變化得到的最大值,\(f[i]\)為以\(i\)結尾的最長上升子序列的長度
則\(f[i]=max(f[i],f[j]+1),j<i,max[j] \leq i,min[i] \geq j\)\
我們會發現這是一個三位偏序問題,可以用\(CDQ\)分治優化
程式碼
#include<cstdio> #include<algorithm> #include<iostream> inline int read(){ int x=0,fh=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') fh=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*fh; } const int maxn=1e6+5; int f[maxn],a[maxn],mmax[maxn],mmin[maxn],tr[maxn],n,m; int lb(int xx){ return xx&-xx; } void ad(int wz,int val){ for(int i=wz;i<maxn;i+=lb(i)){ tr[i]=std::max(tr[i],val); } } int cx(int wz){ int ans=0; for(int i=wz;i>0;i-=lb(i)){ ans=std::max(tr[i],ans); } return ans; } void qk(int wz){ for(int i=wz;i<maxn;i+=lb(i)){ tr[i]=0; } } int tot=1,id[maxn]; bool cmp1(int aa,int bb){ return a[aa]<a[bb]; } bool cmp2(int aa,int bb){ return mmin[aa]<mmin[bb]; } void solve(int l,int r){ if(l==r) return; int mids=(l+r)>>1; solve(l,mids); for(int i=l;i<=r;i++) id[i]=i; std::sort(id+l,id+mids+1,cmp1); std::sort(id+mids+1,id+r+1,cmp2); int now=l; for(int i=mids+1;i<=r;i++){ while(a[id[now]]<=mmin[id[i]] && now<=mids){ ad(mmax[id[now]],f[id[now]]); now++; } f[id[i]]=std::max(f[id[i]],cx(a[id[i]])+1); tot=std::max(tot,f[id[i]]); } for(int i=now-1;i>=l;i--){ qk(mmax[id[i]]); } solve(mids+1,r); } int main(){ n=read(),m=read(); for(int i=1;i<=n;i++){ a[i]=read(); mmax[i]=mmin[i]=a[i]; f[i]=1; } for(int i=1;i<=m;i++){ int aa,bb; aa=read(),bb=read(); mmax[aa]=std::max(mmax[aa],bb); mmin[aa]=std::min(mmin[aa],bb); } solve(1,n); printf("%d\n",tot); return 0; }