2023河南萌新联赛第(五)场:郑州轻工业大学-K 01BFS
https://ac.nowcoder.com/acm/contest/62977/K
文章目录
题目大意
给定一个 n × m ( 1 ≤ n × m ≤ 1 e 6 ) n\times m(1\le n\times m\le 1e6) n×m(1≤n×m≤1e6)的矩阵 G G G每个单元有权值 G i , j ( 1 ≤ G i , j ≤ 1 e 5 ) G_{i,j}(1\le G_{i,j}\le 1e5) Gi,j(1≤Gi,j≤1e5),请选定其中一个单元为起点,再选定一个起始方向(上下左右),可以多次跳到该方向上权值严格大于当前单元权值的单元上,如果最开始选择的为左右方向,可换一次方向(上或下)继续跳,如果最开始选择的为上下方向,可换一次方向(左或右)继续跳,也可以不换方向,求最多能跳多少次?
解题思路
注意到做多只能转一次方向,可以考虑枚举转折点。当某个点 [ x , y ] [x,y] [x,y]作为转折点时有 12 12 12种情况:从该点的四个方向中的一个方向进来,再从剩下三个方向中的一个方向出去。对于任意一个方向进来时,我们可以计算该方向到该点为止的最长上升子序列(可以保证跳的次数最多),对于从任意一个方向出去,可以计算从该点出发的最长上升子序列,枚举 12 12 12种情况,求出最大值。
最长上升子序列
O ( n 2 ) O(n^2) O(n2)做法
设
d
p
i
dp_i
dpi表示终点为
i
i
i的最长上升子序列,转移式:
d
p
i
=
M
a
x
j
<
i
&
a
j
<
a
i
(
d
p
j
)
+
1
dp_i=Max_{j<i \& a_j<a_i}(dp_j)+1
dpi=Maxj<i&aj<ai(dpj)+1
O ( n l o g n ) O(nlogn) O(nlogn)做法
观察到对于 i > j & a i < a j & d p i > d p j i>j\&a_i<a_j\&dp_i>dp_j i>j&ai<aj&dpi>dpj的情况来说, j j j对于后续没有贡献,故可以维护一个单调栈来存储,求 d p i dp_i dpi时只要在单调栈中二分求取第一个大于等于 a i a_i ai的值并替换掉它即可,由于每跳一步贡献都相同,设被替换掉的数在栈中的位数为 k k k, d p i = d p s k − 1 + 1 = d p k = k dp_i=dp_{s_{k-1}}+1=dp_k=k dpi=dpsk−1+1=dpk=k,故可以直接替换 s k s_k sk, d p i = k dp_i=k dpi=k
直接求从某点从任意一个方向出发的最长上升子序列并不容易,可以转化为求该方向上以该点为中点的最长下降子序列,两者是等价的。
注意
本题数据为 n × m ≤ 1 e 6 n\times m\le 1e6 n×m≤1e6,需要用 v e c t o r vector vector开动态数组
代码
#include<bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=1e6+5;
int n,m,T;
int main(){
cin>>T;
while(T--){
cin>>n>>m;
int t;
vector<vector<array<int,9>>>f(n,vector<array<int,9>>(m));
n--,m--;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
cin>>f[i][j][0];
//f[1]第i行以j为终点的向右的最长上升子序列, f[3]第i行以j为终点的向左的最长上升子序列,
// f[2]第i行以j为终点的向右的最长下降子序列, f[4]第i行以j为终点的向左的最长下降子序列,
// f[5]第j列以i为终点的向下的最长上升子序列, f[7]第j列以i为终点的向上的最长上升子序列,
// f[6]第j列以i为终点的向下的最长下降子序列, f[8]第j列以i为终点的向上的最长下降子序列,
for(int i=0;i<=n;i++){
t=0;
vector<int>s;
s.push_back(0);
for(int j=0;j<=m;j++){
int id;
if(s[t]<f[i][j][0])s.pb(f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),f[i][j][0])-s.begin();
s[id]=f[i][j][0];
}
f[i][j][1]=id;
}
s.clear();
t=0;
s.push_back(0);
for(int j=m;j>=0;j--){
int id;
if(s[t]<f[i][j][0])s.pb(f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),f[i][j][0])-s.begin();
s[id]=f[i][j][0];
}
f[i][j][3]=id;
}
s.clear();
s.push_back(-0x3f3f3f3f);
t=0;
for(int j=0;j<=m;j++){
int id;
if(s[t]<-f[i][j][0])s.pb(-f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),-f[i][j][0])-s.begin();
s[id]=-f[i][j][0];
}
f[i][j][2]=id;
}
s.clear();
s.push_back(-0x3f3f3f3f);
t=0;
for(int j=m;j>=0;j--){
int id;
if(s[t]<-f[i][j][0])s.pb(-f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),-f[i][j][0])-s.begin();
s[id]=-f[i][j][0];
}
f[i][j][4]=id;
}
}
for(int j=0;j<=m;j++){
t=0;
vector<int>s;
s.push_back(0);
for(int i=0;i<=n;i++){
int id;
if(s[t]<f[i][j][0])s.pb(f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),f[i][j][0])-s.begin();
s[id]=f[i][j][0];
}
f[i][j][5]=id;
}
s.clear();
t=0;
s.push_back(0);
for(int i=n;i>=0;i--){
int id;
if(s[t]<f[i][j][0])s.pb(f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),f[i][j][0])-s.begin();
s[id]=f[i][j][0];
}
f[i][j][7]=id;
}
s.clear();
s.push_back(-0x3f3f3f3f);
t=0;
for(int i=0;i<=n;i++){
int id;
if(s[t]<-f[i][j][0])s.pb(-f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),-f[i][j][0])-s.begin();
s[id]=-f[i][j][0];
}
f[i][j][6]=id;
}
s.clear();
s.push_back(-0x3f3f3f3f);
t=0;
for(int i=n;i>=0;i--){
int id;
if(s[t]<-f[i][j][0])s.pb(-f[i][j][0]),id=++t;
else{
id=lower_bound(s.begin(),s.end(),-f[i][j][0])-s.begin();
s[id]=-f[i][j][0];
}
f[i][j][8]=id;
}
}
int ma=0;
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
for(int k=1;k<=8;k+=2)
for(int l=2;l<=8;l+=2)
if(l!=k+1)
ma=max(ma,f[i][j][k]+f[i][j][l]-1);
}
cout<<ma<<'\n';
}
}