传送门

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的更多相关文章

  1. 二维树状数组 BZOJ 1452 [JSOI2009]Count

    题目链接 裸二维树状数组 #include <bits/stdc++.h> const int N = 305; struct BIT_2D { int c[105][N][N], n, ...

  2. HDU1559 最大子矩阵 (二维树状数组)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1559 最大子矩阵 Time Limit: 30000/10000 MS (Java/Others)  ...

  3. POJMatrix(二维树状数组)

    Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 22058   Accepted: 8219 Descripti ...

  4. poj 1195:Mobile phones(二维树状数组,矩阵求和)

    Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14489   Accepted: 6735 De ...

  5. Codeforces Round #198 (Div. 1) D. Iahub and Xors 二维树状数组*

    D. Iahub and Xors   Iahub does not like background stories, so he'll tell you exactly what this prob ...

  6. POJ 2155 Matrix(二维树状数组+区间更新单点求和)

    题意:给你一个n*n的全0矩阵,每次有两个操作: C x1 y1 x2 y2:将(x1,y1)到(x2,y2)的矩阵全部值求反 Q x y:求出(x,y)位置的值 树状数组标准是求单点更新区间求和,但 ...

  7. [poj2155]Matrix(二维树状数组)

    Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 25004   Accepted: 9261 Descripti ...

  8. POJ 2155 Matrix (二维树状数组)

    Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 17224   Accepted: 6460 Descripti ...

  9. [POJ2155]Matrix(二维树状数组)

    题目:http://poj.org/problem?id=2155 中文题意: 给你一个初始全部为0的n*n矩阵,有如下操作 1.C x1 y1 x2 y2 把矩形(x1,y1,x2,y2)上的数全部 ...

随机推荐

  1. 关闭会声会影2018提示UEIP.dll找不到指定模块

    最近有一些会声会影2018用户反映在关闭后弹出UEIP.dll错误,不知道该怎么办才好,针对这个问题,小编下面为大家介绍下解决方法. 原因分析 出现这个错误跟会声会影安装路径有中文字符是密切相关的,导 ...

  2. JavaScript学习笔记(五)——类型、转换、相等、字符串

    第六章 类型 相等 转换等 一.类型 1 typeof(); typeof是一个内置的JavaScript运算符,可用于探测其操作数的类型. 例: <script language=" ...

  3. thinkphp5框架生成二维码(二)

    上篇已经讲过了SDK之类的,这个不再重复,有不知道的童鞋们,请去看上篇文章吧. 这里我用的方法比较老旧,大家有更好的方法,可以进行改良,还有linux服务器,记得给文件权限,否则生成的文件会失败的.大 ...

  4. 如何停止AAD服务

    Connect-MsolService (Get-MSOLCompanyInformation).DirectorySynchronizationEnabled 用这个命令查看是enable还是Dis ...

  5. 算法笔记(c++)--桶排序题目

    算法笔记(c++)--桶排序 记得题目是排序,输入n个1-1000的数字然后去重然后排序. 桶排序没毛病 #include<iostream> using namespace std; i ...

  6. ES6的新特性(1)——ES6 的概述

    ES6 的概述 首先,感谢马伦老师的ES6新特性的教程. ECMAScript 和 JavaScript 的关系是 ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前 ...

  7. Kotlin 学习笔记(一)

    (Kotlin 学习笔记的文档结构基本按照 Java 核心技术一书的目录排列) 基本程序设计结构 数据类型 数字 类型 宽度 Double 64 Float 32 Long 64 Int 32 Sho ...

  8. HDU 5642 King's Order dp

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5642 King's Order  Accepts: 381  Submissions: 1361   ...

  9. Beta结束感想

    我得到的: 管理一个9人团队的经验 与组内成员(大部分一开始并不认识)共同向同一个目标努力的宝贵经历 学会使用Github的organization来管理整个团队的代码 学会使用leangoo这样的协 ...

  10. iOS- 全方位解析.crash文件崩溃报告

    1.前言 想来每个iOS攻城狮,都免不了要接触.crash文件 那么什么是.crash文件? iOS app的所有崩溃记录都会记录在设备上,所以对于和我一样没有集成让用户发送崩溃报告功能的iOS开发者 ...