1. 程式人生 > >【二維樹狀數組】【CF10D】 LCIS

【二維樹狀數組】【CF10D】 LCIS

chan wap 傳送門 樹狀 lcis tdi swa long long code

傳送門

Description

給你兩個串,求他們的最長公共上升子序列

Input

第一行是第一個串的長度\(n\)

第二行\(n\)個數代表第一個串

第三行是第二個串的長度\(m\)

第四行\(m\)個數代表第二個串

Output

輸出最長子序列的長度以及方案

Hint

\(For~All:\)

\(0~\leq~n~\leq~500\)

Solutoin

先考慮樸素DP,可以設\(f_{i,j}\)代表第一個串選前\(i\)個,第二個串選前\(j\)個的答案,轉移顯然\(f_{i,j}=\max\{f_{k,h}\}+1\),其中\(k~<~i,j~<~h,A_i=B_j,A_k=B_h\)

。復雜度為\(O(n^4)\),GG。

考慮把這個問題放到一個串上,問題變成了求一個串的LIS,我口胡了一個十分鬼畜的算法:讀入離散化以後,設\(f_i\)為當前算到的以\(i\)這個數結尾的ans,註意這裏\(i\)是代表的是一個位置的值而不是下標。於是當正著掃描的時候,轉移顯然\(f_i=\max\{f_j\}+1\),其中\(j~<~i\)。發現\(f_i\)是從\(f_j\)的前綴\(\max\)轉移過來的。於是可以使用樹狀數組維護這個前綴\(\max\)。具體的,\(f_i=ask(i-1)+1\),然後在樹狀數組上修改\(i\)位置的\(f_i\)

考慮一共枚舉\(n\)個位置,樹狀數組復雜度是\(O(logn)\)

,總復雜度為\(O(nlogn)\)

在這裏放上一個串的樹狀數組LIS代碼

#include<cmath>
#include<cstdio>
#include<algorithm>
#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
    char buf[300];
}

template <typename T>
inline void qr(T &x) {
    rg char ch=getchar(),lst=' ';
    while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
    while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst == '-') x=-x;
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
    if(x < 0) {putchar('-');x=-x;}
    rg int top=0;
    do {
        IO::buf[++top]=x%10+'0';
    } while(x/=10);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T a,const T b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}

template <typename T>
inline void mswap(T &a,T &b) {
    T _temp=a;a=b;b=_temp;
}

const int maxn = 100010;

int n;
int MU[maxn],tree[maxn],frog[maxn],temp[maxn];

int ask(int);
void init_hash();
void change(int,ci);

inline int lowbit(ci x) {return x&(-x);}

int main() {
    qr(n);
    for(rg int i=1;i<=n;++i) {qr(MU[i]);temp[i]=MU[i];}
    init_hash();
    for(rg int i=1;i<=n;++i) {
        frog[MU[i]]=ask(MU[i]-1)+1;
        change(MU[i],frog[MU[i]]);
    }
    qw(n-ask(n),'\n',true);
    return 0;
}

void init_hash() {
    std::sort(temp+1,temp+1+n);
    int *ed=std::unique(temp+1,temp+1+n);
    for(rg int i=1;i<=n;++i) MU[i]=std::lower_bound(temp+1,ed,MU[i])-temp;
}

int ask(int x) {
    int _ans=0;
    while(x) {
        _ans=mmax(_ans,tree[x]);
        x-=lowbit(x);
    }
    return _ans;
}

void change(int x,ci v) {
    while(x <= n) {
        tree[x]=mmax(tree[x],v);
        x+=lowbit(x);
    }
}

回到這個題,我們可以仿照一個串的方式,再加一個維度,設\(f_{i,j}\)第一個串算到當前以\(i\)這個數結尾,第二個串以第\(j\)個位置結尾的\(ans\)。註意這裏\(i\)代表的A串的值,\(j\)代表的是\(b\)串的下標。於是轉移顯然\(f_{i,j}=\max\{f_{k,h}\}+1\),其中\(k~<~i,h<j\)。於是發現\(i,j\)由一個矩形的前綴最大值轉移過來,可以用二維樹狀數組維護這個最大值。這樣復雜度降為\(O(n^2log^2n)\),可以通過本題。

這裏的一個姿勢是二維樹狀數組,他可以維護一個矩形的二維前綴和或前綴最大值。查詢代碼為

Zay ask(ci x,ci y) {
    rg Zay _ret;
    for(rg int i=x;i;i-=lowbit(i)) {
        for(rg int j=y;j;j-=lowbit(j)) {
            _ret=mmax(_ret,tree[i][j]);
        }
    }
    return _ret;
}

修改代碼為

void add(ci x,ci y) {
    Zay _tp=Zay(x,y);
    for(rg int i=x;i<=tcnt;i+=lowbit(i)) {
        for(rg int j=y;j<=tcnt;j+=lowbit(j)) {
            tree[i][j]=mmax(tree[i][j],_tp);
        }
    }
}

更一般的,修改代碼中_tp可以改為一個傳進來的參數代表要修改位置的值。

於是這個題就完了。

Code

#include<cmath>
#include<cstdio>
#include<algorithm>
#define rg register
#define ci const int
#define cl const long long int

typedef long long int ll;

namespace IO {
    char buf[300];
}

template <typename T>
inline void qr(T &x) {
    rg char ch=getchar(),lst=' ';
    while((ch > '9') || (ch < '0')) lst=ch,ch=getchar();
    while((ch >= '0') && (ch <= '9')) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(lst == '-') x=-x;
}

template <typename T>
inline void qw(T x,const char aft,const bool pt) {
    if(x < 0) {putchar('-');x=-x;}
    rg int top=0;
    do {
        IO::buf[++top]=x%10+'0';
    } while(x/=10);
    while(top) putchar(IO::buf[top--]);
    if(pt) putchar(aft);
}

template <typename T>
inline T mmax(const T &a,const T &b) {return a > b ? a : b;}
template <typename T>
inline T mmin(const T a,const T b) {return a < b ? a : b;}
template <typename T>
inline T mabs(const T a) {return a < 0 ? -a : a;}

template <typename T>
inline void mswap(T &a,T &b) {
    T _temp=a;a=b;b=_temp;
}

const int maxn = 1010;
const int maxd = 1010;

int n,m,tcnt;
int a[maxn],b[maxn],MU[maxd],frog[maxd][maxd],remap[maxd];

struct Zay {
    int x,y;
    inline bool operator<(const Zay &_others) const {
        return frog[this->x][this->y] < frog[_others.x][_others.y];
    }
    inline bool operator>(const Zay &_others) const {
        return frog[this->x][this->y] > frog[_others.x][_others.y];
    }
    Zay (ci _a=0,ci _b=0) {x=_a,y=_b;}
};
Zay tree[maxd][maxd],pre[maxn][maxn],ans;

Zay ask(ci,ci);
void init_hash();
void add(ci,ci);
void dfs(ci,ci);

inline int lowbit(ci x) {return x&(-x);}

int main() {
    qr(n);
    for(rg int i=1;i<=n;++i) {qr(a[i]);MU[++tcnt]=a[i];}
    qr(m);
    for(rg int i=1;i<=m;++i) {qr(b[i]);MU[++tcnt]=b[i];}
    init_hash();
    for(rg int i=1;i<=n;++i) {
        for(rg int j=1;j<=m;++j) if(a[i] == b[j]) {
            Zay _temp=ask(a[i]-1,j-1);
            frog[a[i]][j]=frog[_temp.x][_temp.y]+1;
            pre[a[i]][j]=_temp;
            add(a[i],j);
            ans=mmax(ans,(Zay){a[i],j});
        }
    }
    qw(frog[ans.x][ans.y],'\n',true);
    dfs(ans.x,ans.y);
    return 0;
}

void init_hash() {
    std::sort(MU+1,MU+1+tcnt);
    int *ed=std::unique(MU+1,MU+1+tcnt);
    rg int k;
    for(rg int i=1;i<=n;++i) k=std::lower_bound(MU+1,ed,a[i])-MU,remap[k]=a[i],a[i]=k;
    for(rg int i=1;i<=m;++i) k=std::lower_bound(MU+1,ed,b[i])-MU,remap[k]=b[i],b[i]=k;
}

Zay ask(ci x,ci y) {
    rg Zay _ret;
    for(rg int i=x;i;i-=lowbit(i)) {
        for(rg int j=y;j;j-=lowbit(j)) {
            _ret=mmax(_ret,tree[i][j]);
        }
    }
    return _ret;
}

void add(ci x,ci y) {
    Zay _tp=Zay(x,y);
    for(rg int i=x;i<=tcnt;i+=lowbit(i)) {
        for(rg int j=y;j<=tcnt;j+=lowbit(j)) {
            tree[i][j]=mmax(tree[i][j],_tp);
        }
    }
}

void dfs(ci x,ci y) {
    if(!y) return;
    dfs(pre[x][y].x,pre[x][y].y);
    qw(remap[b[y]],' ',true);
}

Summary

二維樹狀數組查詢和修改的操作。

【二維樹狀數組】【CF10D】 LCIS