Description
給出
N 個正整數a[1…N] ,再給出K 個關係符號(>、<或=)s[1…k] 。選出一個長度爲L 的子序列,要求這個子序列的第i 項和第i+1 項的的大小關係爲s[(i−1)%K+1] 。求出L 的最大值。Input
- 第一行兩個正整數,分別表示
N 和K(N,K≤5∗105) 。- 第二行給出
N 個正整數,第i個正整數表示ai(ai≤106) 。- 第三行給出
K 個空格隔開關係符號(>、<或=),第i 個表示si 。Output
- 一個正整數,表示
L 的最大值。- 第二行
L 個整數,表示一種方案序列(Multiple Answers)。Sample Input
7 3
2 4 3 1 3 5 3
< > =Sample Output
6
2 4 3 3 5 3
一開始我是這麼想的:對於當前每個值,枚舉以它結尾可能的子序列長度,然後再判斷前面是否有數能滿足。這樣的複雜度是
在考慮優化的時候,當前的最優解分別從所有滿足
然而實際上並不需要考慮的這麼麻煩,定義
- 該點的最優解一定是從前面某位置的最優解更新來的(即如果要從
Ai 來更新,則一定是所有以它爲末尾元素的合法子序列中最長的那條,也即dp[i] )。
講着是推論其實我不太清楚是怎麼推的。
(16/10/4晚更新)終於基本搞清楚怎麼推了,感謝焱犇神牛的證明。
爲證明上述命題,我們需證下圖所示的命題:
- 對於點
i 可能繼承的兩條子序列Long 與Short ,當j 所在的子序列位置不是最優解dp[j]=lengthLong 時,dp[i] 不可能達到最優。
此時對於
y<x (注意x−y≥1 )。dp[i]=y+1 ,且i 無法從Long 序列繼承。
根據上述條件,我們得到以下推論:
位置
x 與位置y 的符號不同。
假設其相同。由於兩個位置上的值同爲aj ,那麼顯然Long 序列是可以代替Short 序列的,Short 序列不會比繼承Long 序列優。所以aj 與ai 一定滿足y 位置上的符號關係,而不滿足x 位置上的符號關係。aj 與ai 一定不相等。
假設其相等。若位置x 爲′=′ ,則一定選Long序列繼承;若位置x 不爲′=′ ,由於相等,i 仍然可以從x−1 轉移過來,由於x−1≥y ,所以選Long 序列不會比Short 差,不必從Short 序列繼承y 。位置
y 的符號一定不是′=′ 。
因爲由上述兩推論可以知道,由於一定要從Short 序列更新,且aj≠ai ,所以一定不是′=′ 。
我們可以在
ak 與ai 不滿足位置y 的符號關係。
顯然如果滿足了那就不必從Short 序列更新了。
根據上述結論:
- 假設
x 位置是小於或等於號:y 位置的符號就是大於號(推論1,推論3),所以ak<ai (推論4)。由於x 位置無法被i 繼承,所以應該有aj>ai (推論2)。緊接着我們就可以得到又因爲ak<ai<aj(k<j<i) k 位置的符號是大於號,所以必然能找到且ak>al<aj(k<l<j) l 位置的符號必然是′<′ 。那麼現在,dp[k]=y ,dp[l]=y+1 ,dp[i]=y+2>y+1 。 - 假設
x 位置是小於或等於號,同理可證依舊有dp[i]>y+1 。
綜上所述,無論
這TM能做?
後面就簡單多了。
線段樹:
#include <bits/stdc++.h>
#define M 500005
#define P 1000000
using namespace std;
int A[M];char str[M],buf[5];
struct Segment{
int tree[P+5<<2];
void up(int p){tree[p]=max(tree[p<<1],tree[p<<1|1]);}
void update(int L,int R,int x,int val,int p){
if(L==R){
tree[p]=max(tree[p],val);
return;
}
int mid=L+R>>1;
if(x<=mid)update(L,mid,x,val,p<<1);
else update(mid+1,R,x,val,p<<1|1);
up(p);
}
int query(int L,int R,int l,int r,int p){
if(l>r)return 0;
if(L==l&&R==r)return tree[p];
int mid=L+R>>1;
if(r<=mid)return query(L,mid,l,r,p<<1);
else if(l>mid)return query(mid+1,R,l,r,p<<1|1);
else return max(query(L,mid,l,mid,p<<1),query(mid+1,R,mid+1,r,p<<1|1));
}
}Mx,Mi;//> <
int Past[P+5],dp[M];
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
for(int i=1;i<=m;i++){
scanf("%s",buf);str[i]=buf[0];
}
int ans=0,tot=0;
for(int i=1;i<=n;i++){
dp[i]=dp[Past[A[i]]]+1;
dp[i]=max(dp[i],Mx.query(1,P,A[i]+1,P,1)+1);
dp[i]=max(dp[i],Mi.query(1,P,1,A[i]-1,1)+1);
if(ans<dp[i])ans=dp[i],tot=i;
char c=str[(dp[i]-1)%m+1];
if(c=='=')Past[A[i]]=i;
else if(c=='>')Mx.update(1,P,A[i],dp[i],1);
else Mi.update(1,P,A[i],dp[i],1);
}
// for(int i=0;i<=n;i++)printf("%d\n",dp[i]);
printf("%d\n",ans);
stack<int>Ans;
int val=ans,hre=A[tot];
Ans.push(hre);
for(;tot;--tot)
if(dp[tot]+1==val){
char c=str[(dp[tot]-1)%m+1];
if(c=='='&&A[tot]==hre||c=='>'&&A[tot]>hre||c=='<'&&A[tot]<hre){
val--;hre=A[tot];Ans.push(hre);
}
}
while(!Ans.empty())printf("%d%c",Ans.top(),Ans.size()==1?'\n':' '),Ans.pop();
}
樹狀數組:
#include <bits/stdc++.h>
#define M 500005
#define P 1000000
using namespace std;
int A[M];char str[M],buf[5];
struct Binary_Indexed{
int tree[P+5];
#define lowbit(x) x&(-x)
void add_Mi(int pos,int val){
while(pos<=P){
tree[pos]=max(val,tree[pos]);
pos+=lowbit(pos);
}
}
int query_Mi(int pos){
int ans=0;
while(pos){
ans=max(ans,tree[pos]);
pos-=lowbit(pos);
}
return ans;
}
void add_Mx(int pos,int val){
while(pos){
tree[pos]=max(val,tree[pos]);
pos-=lowbit(pos);
}
}
int query_Mx(int pos){
int ans=0;
while(pos<=P){
ans=max(ans,tree[pos]);
pos+=lowbit(pos);
}
return ans;
}
}Mx,Mi;
int Past[P+5],dp[M];
int main(){
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&A[i]);
for(int i=1;i<=m;i++){
scanf("%s",buf);
str[i]=buf[0];
}
int ans=0,tot=0;
for(int i=1;i<=n;i++){
dp[i]=dp[Past[A[i]]]+1;
dp[i]=max(dp[i],Mx.query_Mx(A[i]+1)+1);
dp[i]=max(dp[i],Mi.query_Mi(A[i]-1)+1);
if(ans<dp[i])ans=dp[i],tot=i;
char c=str[(dp[i]-1)%m+1];
if(c=='=')Past[A[i]]=i;
else if(c=='>')Mx.add_Mx(A[i],dp[i]);
else Mi.add_Mi(A[i],dp[i]);
}
printf("%d\n",ans);
stack<int>Ans;
int val=ans,hre=A[tot];
Ans.push(hre);
for(;tot;--tot)
if(dp[tot]+1==val){
char c=str[(dp[tot]-1)%m+1];
if(c=='='&&A[tot]==hre||c=='>'&&A[tot]>hre||c=='<'&&A[tot]<hre){
val--;hre=A[tot];Ans.push(hre);
}
}
while(!Ans.empty())
printf("%d%c",Ans.top(),Ans.size()==1?'\n':' '),Ans.pop();
}