YbtOJ 「数据结构」 第3章 RMQ问题

st表

A. 【例题1】数列区间

静态区间最值常用算法 设置st[i][j]表示从i节点开始 向后2j个元素的最大/最小值 也就是[i,i+2j1]中区间的最值

预处理f[i][j]=max(f[i][j1],f[i+(1<<(j1))][j1]);

查询x,y区间时 先定义llg[rl+1] 查询的是max/min(st[x][l],st[y(1<<l)+1][l])

+1和-1都要分清

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'

#define inl inline

const int N = 2e5 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , lg[N] = {-1} , st[N][31];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) lg[i] = lg[i>>1] + 1;
	for ( int i = 1 ; i <= n ; i ++ ) st[i][0] = read();
	for ( int j = 1 ; j <= lg[n] ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ ) 
			st[i][j] = max ( st[i][j-1] , st[i+(1<<(j-1))][j-1] );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int l = read() , r = read();
		int k = lg[r-l+1];
		cout << max ( st[l][k] , st[r-(1<<k)+1][k] ) << endl;
	}
	return 0;
}

B. 【例题2】静态区间

维护的时候直接将求最大值改成求gcd 只是一个轻易的优化

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'

#define inl inline

const int N = 2e5 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , lg[N] = {-1} , st[N][31];

int gcd ( int a , int b )
{
	if ( a % b == 0 ) return b;
	return gcd ( b % a , a );
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) lg[i] = lg[i>>1] + 1;
	for ( int i = 1 ; i <= n ; i ++ ) st[i][0] = read();
	for ( int j = 1 ; j <= lg[n] ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ ) 
			st[i][j] = gcd ( st[i][j-1] , st[i+(1<<(j-1))][j-1] );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int l = read() , r = read() , k = lg[r-l+1];
		cout << gcd ( st[l][k] , st[r-(1<<k)+1][k] ) << endl;
	}
	return 0;
}

C. 【例题3】与众不同

[题目描述]

A 是某公司的 CEO,每个月都会有员工把公司的盈利数据送给 A,A 是个与众不同的怪人,A 不注重盈利还是亏本,而是喜欢研究「完美序列」:一段连续的序列满足序列中的数互不相同。

A 想知道区间[L,R]之间最长的完美序列长度。

[输入格式]

第一行两个整数N,M,表示连续N个月,编号为0N1, 表示询问的次数;

第二行N个整数,第 i数表示该公司第i个月的盈利值 ;

接下来M行每行两个整数 ,表示 A 询问的区间。

[输出格式]

输出M行,每行一个整数对应询问区间内的完美序列的最长长度。

[算法分析]

要么是标程错了 要么是评测机寄了 要么就是YbtOJ出题人纯纯nt

下面放的代码是样例死循环但是100pts的代码 正常加了特判考虑多种情况的代码死活过不去 要么CE 要么20pts

采用复制调试法后得到是初始化lg[N]={1}导致出锅 在主函数中初始化lg[0]=1即可AC 不过我想问我的写法错在哪儿了?????????????


进入正题 思路搞明白就可以了

我们记录三个数组:

  1. f[i]表示以i为结尾的最长完美序列的长度
  2. pre[i]表示以i为结尾的完美序列的起始位置
  3. last[val]表示上一个val的位置

可以在O(n)时间内求出这三个数组 转移见代码

查询:

假设我们要找[l,r]区间中的最长“完美序列”

pre的值是一个非递减的序列 那么可以考虑二分 二分出最靠前的 满足pre[i]在[l,r]区间内的下标 pos

那么此时区间[l,pos1]pre[i]值都在l左面 而[pos,r]区间内的pre[i]都在[l,r]区间内

这样就把区间分成了两部分 那么答案就是max(posl,query(pos,r)) 其中query是查询这一段区间内f[i](也就是区间长度)的最大值

[代码实现]

#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define endl '\n'
#define mid ((l+r)>>1)
const int N = 2e6 + 10;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N] , lg[N] , st[N][30] , last[N] , f[N] , pre[N];

int query ( int l , int r )
{
	int k = lg[r-l+1];
	return max ( st[l][k] , st[r-(1<<k)+1][k] );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	lg[0] = -1;
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) lg[i] = lg[i>>1] + 1;
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read();
		pre[i] = max ( pre[i-1] , last[a[i]] + 1 );
		last[a[i]] = i;
		f[i] = i - pre[i] + 1;
		st[i][0] = f[i];
	}
	for ( int j = 1 ; j <= lg[n] ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ )
			st[i][j] = max ( st[i][j-1] , st[i+(1<<(j-1))][j-1] );
	for ( int i = 1 , x , y , l , r ; i <= m ; i ++ )
	{
		l = x = read() + 1 , r = y = read() + 1;
		while ( l <= r )
		{
			if ( pre[mid] < x ) l = mid + 1;
			else r = mid - 1;
		}
		cout << max ( l - x , query ( l , y ) ) << endl;
	}
	return 0;
}

较为完备的代码(实际上应该在初始读入的时候整体权值+K避免负数下标 不过出题人都这样fw了我们就不要苛责太多了)

#include <bits/stdc++.h>
using namespace std;
#define inl inline
#define endl '\n'
#define mid ((l+r)>>1)
#define int long long 
const int N = 2e6 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N] , lg[N] , st[N][20] , last[N] , f[N] , pre[N];

int query ( int l , int r )
{
	int k = lg[r-l+1];
	return max ( st[l][k] , st[r-(1<<k)+1][k] );
}


signed main ()
{
	lg[0]=-1;
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) lg[i] = lg[i>>1] + 1;
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = read();
		pre[i] = max ( pre[i-1] , last[a[i]] + 1 );
		last[a[i]] = i;
		f[i] = i - pre[i] + 1;
		st[i][0] = f[i];
	}
	for ( int j = 1 ; j <= lg[n] ; j ++ )
		for ( int i = 1 ; i + ( 1 << j ) - 1 <= n ; i ++ )
			st[i][j] = max ( st[i][j-1] , st[i+(1<<(j-1))][j-1] );
	for ( int i = 1 , x , y , l , r ; i <= m ; i ++ )
	{
		l = x = read() + 1 , r = y = read() + 1;
		if ( pre[y] <= x ) { cout << y - x + 1 << endl; continue; }
		while ( l <= r )
		{
			if ( pre[mid] < x ) l = mid + 1;
			else r = mid - 1;
		}
		cout << max ( ( l - 1 ) - x + 1 , query ( l , y ) ) << endl;
	}
	return 0;
}

D. 【例题4】矩阵最值

[题目描述]

我们有一个nm列的矩阵,现在小Q有k个问题,每次询问一个以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的最大值。

[输入格式]

第一行三个整数n,m,k

接下来n行,每行有m个整数,设ai.j为矩阵ij列的数字。

接下来k行,每行4个整数x1,y1,x2,y2

[输出格式]

k行,每行对应一个答案

[代码实现]

注意这里的k不能取等只是因为需要防止数组越界

#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 255;

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}

int n , m , K , lg[N] , st[N][N][20][20];
int query ( int x11 , int y11 , int x22 , int y22 )
{
	int lx = lg[x22-x11+1] , ly = lg[y22-y11+1];
	return max ( max ( st[x11][y11][lx][ly] , st[x11][y22-(1<<ly)+1][lx][ly] ) , max ( st[x22-(1<<lx)+1][y11][lx][ly] , st[x22-(1<<lx)+1][y22-(1<<ly)+1][lx][ly] ) );
	
}

signed main ()
{
	//freopen ( "a.in" , "r" , stdin );
	lg[0] = -1;
	n = read() , m = read() , K = read();
	for ( int i = 1 ; i <= max ( n , m ) ; i ++ ) lg[i] = lg[i>>1] + 1;
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ )
			st[i][j][0][0] = read();
	for ( int k = 0 ; k <= lg[n] ; k ++ )
		for ( int l = 0 ; l <= lg[m] ; l ++ )
			for ( int i = 1 ; i + ( 1 << k ) - 1 <= n ; i ++ )
				for ( int j = 1 ; j + ( 1 << l ) - 1 <= m ; j ++ )
				{
					if ( k ) st[i][j][k][l] = max ( st[i][j][k][l] , max ( st[i][j][k-1][l] , st[i+(1<<(k-1))][j][k-1][l] ) );
					if ( l ) st[i][j][k][l] = max ( st[i][j][k][l] , max ( st[i][j][k][l-1] , st[i][j+(1<<(l-1))][k][l-1] ) );
				}
	for ( int i = 1 , x11 , y11 , x22 , y22 ; i <= K ; i ++ )
	{
		x11 = read() , y11 = read() , x22 = read() , y22 = read();
		printf ( "%d\n" , query ( x11 , y11 , x22 , y22 ) );
	}
	return 0;
}
posted @   Echo_Long  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示