BZOJ2090 POI2010 Monotonicity


POI2010 題解整理

Description

給出N 個正整數a[1N] ,再給出K 個關係符號(>、<或=)s[1k] 。選出一個長度爲L子序列,要求這個子序列的第i 項和第i+1 項的的大小關係爲s[(i1)%K+1] 。求出L 的最大值。

Input

  • 第一行兩個正整數,分別表示NK(N,K5105)
  • 第二行給出N 個正整數,第i個正整數表示ai(ai106)
  • 第三行給出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


Solution

一開始我是這麼想的:對於當前每個值,枚舉以它結尾可能的子序列長度,然後再判斷前面是否有數能滿足。這樣的複雜度是O(N2logN) ,搞笑一般的暴力。

在考慮優化的時候,當前的最優解分別從所有滿足A[j] (str[Len1] (<、>和=)) A[i]{A[j],Len} 這個二元組中去更新當前的值,然後還要分別得到A[i] 以三種符號結尾的最大長度。前面的更新只需要線段樹維護(也就是下面的代碼),而後面的更新就發現異常困難。

然而實際上並不需要考慮的這麼麻煩,定義dp[i] 表示以Ai 作爲結尾,最長的子序列長度。那麼有個玄學的推論:

  • 該點的最優解一定是從前面某位置的最優解更新來的(即如果要從Ai 來更新,則一定是所有以它爲末尾元素的合法子序列中最長的那條,也即dp[i] )。

講着是推論其實我不太清楚是怎麼推的


(16/10/4晚更新)終於基本搞清楚怎麼推了,感謝焱犇神牛的證明。

爲證明上述命題,我們需證下圖所示的命題:

  • 對於點i 可能繼承的兩條子序列LongShort ,當j 所在的子序列位置不是最優解dp[j]=lengthLong 時,dp[i] 不可能達到最優。

這裏寫圖片描述

此時對於Long 序列,設j 出現的位置爲xShort 序列中出現的位置爲y ,則:

  • y<x (注意xy1 )。
  • dp[i]=y+1 ,且i 無法從Long 序列繼承。

根據上述條件,我們得到以下推論:

  1. 位置x 與位置y 的符號不同。
    假設其相同。由於兩個位置上的值同爲aj ,那麼顯然Long 序列是可以代替Short 序列的,Short 序列不會比繼承Long 序列優。所以ajai 一定滿足y 位置上的符號關係,而不滿足x 位置上的符號關係。

  2. ajai 一定不相等。
    假設其相等。若位置x= ,則一定選Long序列繼承;若位置x 不爲= ,由於相等,i 仍然可以從x1 轉移過來,由於x1y ,所以選Long 序列不會比Short 差,不必從Short 序列繼承y

  3. 位置y 的符號一定不是=
    因爲由上述兩推論可以知道,由於一定要從Short 序列更新,且ajai ,所以一定不是=

這裏寫圖片描述

我們可以在Long 序列中找到位置y 對應的原序列位置k 。那麼有結論:

  • akai 不滿足位置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]=ydp[l]=y+1dp[i]=y+2>y+1
  • 假設x 位置是小於或等於號,同理可證依舊有dp[i]>y+1

綜上所述,無論Shorty 位置的符號如何,我們總能在Long 內找到一點,使得i 可以從該點繼承,並且該點可以繼承Long 內的y 位置,保證最優結果一定y+2

這TM能做


後面就簡單多了。

dp[i]=maxdp[j]A[j]>A[i],str[dp[j]]=>A[j]=A[i],str[dp[j]]==A[j]<A[i],str[dp[j]]=<
分別採用能夠區間查詢最值的數據結構維護一下就可以了,下面給出線段樹和樹狀數組(寫樹狀數組的一般都很神奇)的寫法。

線段樹

#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();
}
發佈了48 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章