zoukankan      html  css  js  c++  java
  • 【二维树状数组】【CF10D】 LCIS

    传送门

    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),'
    ',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],'
    ',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

    二维树状数组查询和修改的操作。

  • 相关阅读:
    macOS Big Sur 关闭 SIP 正确方法
    绕过CDN寻找真实IP的8种方法
    mycat 链接mysql提示ERROR 3009 (HY000): java.lang.IllegalArgumentExceptio解决方式
    【SpringBoot】SpringBoot 内嵌容器及性能
    【Java】基于NIO的聊天室
    【Java】基于BIO的聊天室
    【ZooKeeper】ZooKeeper源码编译
    【SpringBoot】 使用外部Tomcat方法及启动原理
    【Java】深入理解SPI机制
    【SpringBoot】SpringBoot 自动配置原理
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9834008.html
Copyright © 2011-2022 走看看