2023河南萌新联赛第(五)场:郑州轻工业大学-K 01BFS

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(1n×m1e6)的矩阵 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(1Gi,j1e5),请选定其中一个单元为起点,再选定一个起始方向(上下左右),可以多次跳到该方向上权值严格大于当前单元权值的单元上,如果最开始选择的为左右方向,可换一次方向(上或下)继续跳,如果最开始选择的为上下方向,可换一次方向(左或右)继续跳,也可以不换方向,求最多能跳多少次?

解题思路

注意到做多只能转一次方向,可以考虑枚举转折点。当某个点 [ 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=dpsk1+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×m1e6,需要用 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';
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值