【二维树状数组】【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),'\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

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

原文地址:https://www.cnblogs.com/yifusuyi/p/9834008.html

时间: 2024-10-16 23:59:25

【二维树状数组】【CF10D】 LCIS的相关文章

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi

POJ 1195 Mobile phones(二维树状数组)

题目链接:POJ 1195 题意: 给出一个S*S的矩阵(行.列号从1开始),每个元素初始值为0,有两种操作:一种是第X行第Y列元素值加A:另一种是查询给定范围矩阵的所有元素之和(L<=X<=R,B<=Y<=T). 分析: 查询给定范围矩阵的所有元素之和是二维区间和,可以转换为二维前缀和求值.类比一维前缀和求法,二维区间和S(L, B, R, T) = S(1, 1, R, T) - S(1 ,1, L-1, T) - S(1, 1, R, B-1) + S(1, 1, L-1,

POJ 2155 Matrix(二维树状数组,绝对具体)

Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20599   Accepted: 7673 Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1

HDU 5465 Clarke and puzzle Nim游戏+二维树状数组

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5465 Clarke and puzzle Accepts: 42 Submissions: 269 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) 问题描述 克拉克是一名人格分裂患者.某一天,有两个克拉克(aa和bb)在玩一个方格游戏. 这个方格是一个n*mn∗m的矩阵,每个格子里有一

HDOJ 4456 Crowd 离散化+二维树状数组

将坐标旋转45度就可以得到正方形,可以用二维树状数组求解... 为了节省内存,提前将树状数组中会被更新的点全都存下来,并离散化 Crowd Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1199    Accepted Submission(s): 282 Problem Description City F in the south

POJ2029:Get Many Persimmon Trees(二维树状数组)

Description Seiji Hayashi had been a professor of the Nisshinkan Samurai School in the domain of Aizu for a long time in the 18th century. In order to reward him for his meritorious career in education, Katanobu Matsudaira, the lord of the domain of

POJ1195 Mobile phones 【二维树状数组】

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14288   Accepted: 6642 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The

MooFest_二维树状数组

Description Every year, Farmer John's N (1 <= N <= 20,000) cows attend "MooFest",a social gathering of cows from around the world. MooFest involves a variety of events including haybale stacking, fence jumping, pin the tail on the farmer,

HDU_4456_二维树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4456 第一道二维树状数组就这么麻烦,题目要计算的是一个菱形范围内的和,于是可以把原来的坐标系旋转45度,就是求一个正方形范围内的和,这里还涉及到坐标系平移和放大,由于题目数据较大,用了离散化来保存需要处理的点,放在h数组内,对应的二维树状数组存在tree数组内. #include<iostream> #include<cstring> #include<cstdio> #includ