[FROM WOJ]#2210 上升序列

題面
給一個長度10^5的非負序列,序列中的0可以任意換成任何數字(包括負數),問最長嚴格上升子序列長度。

輸入
第一行有一個數n代表序列長度
第二行有n個數字ai代表序列每個值是多少。

輸出
一行一個數字代表答案

樣例輸入
7
2 0 2 1 2 0 5

樣例輸出
5

數據規模
30pts:n<=500030pts: n<=5000
100pts:n<=105,ai<=106100pts: n<=10^5 ,ai<=10^6

SOL
狀態轉移方程:
f[i]=max(f[j]+1),(i>ja[i]>a[j])f[i]=max(f[j]+1),(i>j且a[i]>a[j])
如果沒有0,那麼我們直接加上一個樹狀數組優化(線段樹或者二分什麼的也行)就可以過了
對於這道題,顯然,我們選擇最長的子序列向其中加0最優
但是可能出現多條最長子序列
爲了抵消這部分的影響,我們可以考慮對每一個非0的ai減去其前面0的個數再求
最後加上0的個數
等效於強制所有0都有貢獻,因爲強制一個0出現最多隻能使一個原來最長子序列裏的數不出現,所以不會影響答案

代碼:

#include<bits/stdc++.h>
using namespace std;
#define re register
inline int rd(){
	int re data=0;static char ch=0;ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+(ch^48),ch=getchar();
	return data;
}
const int N=1e5+5,M=1e5,S=1.1e6+1;
int n,a[N],f[N],z[N],ans,c[S];
#define lb(x) (x&-x)
inline void edit(int x,int v){for(;x<S;x+=lb(x))c[x]=max(c[x],v);}
inline int ask(int x,int re ret=0){for(;x;x-=lb(x))ret=max(ret,c[x]);return ret;}
signed main(){
	n=rd();
	for(int re i=1;i<=n;++i)a[i]=rd(),z[i]=(!a[i])?z[i-1]+1:z[i-1];
	for(int re i=1;i<=n;++i){
		if(!a[i])continue;
		a[i]-=z[i],f[i]=ask(a[i]+n-1)+1,edit(a[i]+n,f[i]),ans=max(ans,f[i]);
	}cout<<ans+z[n],exit(0);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章