題目描述 佳媛姐姐過生日的時候,她的小伙伴從某寶上買了一個有趣的玩具送給他。玩具上有一個數列,數列中某些項的值可能會變化,但同一個時刻最多只有一個值發生變化。現在佳媛姐姐已經研究出了所有變化的可能性,她想請教你,能否選出一個子序列,使得在任意一種變化中,這個子序列都是不降的?請你告訴她這個子序列的最 ...
題目描述
佳媛姐姐過生日的時候,她的小伙伴從某寶上買了一個有趣的玩具送給他。玩具上有一個數列,數列中某些項的值可能會變化,但同一個時刻最多只有一個值發生變化。現在佳媛姐姐已經研究出了所有變化的可能性,她想請教你,能否選出一個子序列,使得在任意一種變化中,這個子序列都是不降的?請你告訴她這個子序列的最長長度即可 。
註意:每種變化最多只有一個值發生變化。在樣例輸入1中,所有的變化是:
1 2 3
2 2 3
1 3 3
1 1 3
1 2 4
選擇子序列為原序列,即在任意一種變化中均為不降子序列在樣例輸入2中,所有的變化是:
3 3 3
3 2 3
選擇子序列為第一個元素和第三個元素,或者第二個元素和第三個元素,均可滿足要
輸入輸出格式
輸入格式:
輸入的第一行有兩個正整數n, m,分別表示序列的長度和變化的個數。接下來一行有n個數,表示這個數列原始的狀態。接下來m行,每行有2個數x, y,表示數列的第x項可以變化成y這個值。1 <= x <= n。
輸出格式:
輸出一個整數,表示對應的答案
輸入輸出樣例
輸入樣例#1: 複製3 4 1 2 3 1 2 2 3 2 1 3 4輸出樣例#1: 複製
3
說明
對於20%數據所有數字均為正整數,且小於等於300
對於50%數據所有數字均為正整數,且小於等於3,000
對於100%數據所有數字均為正整數,且小於等於100,000
這道題是DP應該不難看出來。
$dp[i]$表示選擇$i$以後所能形成的滿足條件的子序列的最大值
轉移的時候枚舉前面的點$(j)$。
設$MX[i]$表示$i$號位置能變成的最大值,$MI[i]$表示$i$號位置能變成的最小值,$a$為原序列
這樣轉移的時候會有兩個限制條件
$a[i]>=MX[j]$ && $MI[i]>=a[j]$
這很明顯是個二維偏序問題嘛,用CDQ樹套樹什麼的都可以搞。
樹套樹的話,將$a$抽象為$x$軸,將$MX$抽象為$y$軸
轉移的時候我們實際是在左下角為$(0,0)$,右上角為$MI[i],a[i]$的矩陣中查最大值
每次轉移對答案的貢獻的話實際上只是改變了$a[i],mx[i]$的值
然後就能很自然的想到樹套樹了,線段樹套線段樹或者樹狀數組套線段樹都可以搞
後者常數小一些
線段樹的數組一定要開的足夠大!!!!
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int MAXN=6*1e6+10; const int MAXNN=1e5+10; const int INF=1e8+10; inline int read() { char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int root[MAXN],N,M,MX[MAXNN],MI[MAXNN],a[MAXNN]; struct S { struct node { int ls,rs,mx; }T[MAXN]; int tot; int query(int now,int ll,int rr,int pos) { if(ll==rr) return T[now].mx; int mid=ll+rr>>1; if(pos<=mid) return query(T[now].ls,ll,mid,pos); else return max( T[T[now].ls].mx , query(T[now].rs,mid+1,rr,pos)); } void change(int &now,int ll,int rr,int pos,int val) { if(!now) now=++tot; T[now].mx=max(T[now].mx,val); if(ll==rr) return ; int mid=ll+rr>>1; if(pos<=mid) change(T[now].ls,ll,mid,pos,val); else change(T[now].rs,mid+1,rr,pos,val); } }tree; struct B { int N; int Tree[MAXNN]; int lowbit(int p) {return p&(-p);} int Query(int k,int val) { int ans=0; while(k) { ans=max(ans,tree.query(root[k],1,N,val)); k-=lowbit(k); } return ans; } void Change(int k,int pos,int val) { while(k<=N) { tree.change(root[k],1,N,pos,val); k+=lowbit(k); } } }BIT; int main() { //freopen("heoi2016_seq.in","r",stdin); //freopen("heoi2016_seq.out","w",stdout); N=read();M=read(); for(int i=1;i<=N;i++) MX[i]=MI[i]=a[i]=read(); for(int i=1;i<=M;i++) { int x=read(),y=read(); MX[x]=max(MX[x],y);BIT.N=max(BIT.N,MX[x]); MI[x]=min(MI[x],y); } int ans=0; for(int i=1;i<=N;i++) { int now=BIT.Query(MI[i],a[i])+1; BIT.Change(a[i],MX[i],now); ans=max(ans,now); } printf("%d",ans); return 0; }