YbtOJ 「数据结构」 第3章 RMQ问题
st表
A. 【例题1】数列区间
静态区间最值常用算法 设置\(st[i][j]\)表示从\(i\)节点开始 向后\(2^j\)个元素的最大/最小值 也就是\([i,i+2^j-1]\)中区间的最值
预处理\(f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);\)
查询\(x,y\)区间时 先定义\(l为\)\(lg[r-l+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\)个月,编号为\(0\)到\(N-1\), 表示询问的次数;
第二行\(N\)个整数,第 \(i\)数表示该公司第\(i\)个月的盈利值 ;
接下来\(M\)行每行两个整数 ,表示 A 询问的区间。
[输出格式]
输出\(M\)行,每行一个整数对应询问区间内的完美序列的最长长度。
[算法分析]
要么是标程错了 要么是评测机寄了 要么就是\(YbtOJ\)出题人纯纯\(nt\)
下面放的代码是样例死循环但是\(100pts\)的代码 正常加了特判考虑多种情况的代码死活过不去 要么\(CE\) 要么\(20pts\)
采用复制调试法后得到是初始化\(lg[N]=\{-1\}\)导致出锅 在主函数中初始化\(lg[0]=-1\)即可\(AC\) 不过我想问我的写法错在哪儿了?????????????
进入正题 思路搞明白就可以了
我们记录三个数组:
- \(f[i]\)表示以\(i\)为结尾的最长完美序列的长度
- \(pre[i]\)表示以\(i\)为结尾的完美序列的起始位置
- \(last[val]\)表示上一个\(val\)的位置
可以在\(O(n)\)时间内求出这三个数组 转移见代码
查询:
假设我们要找\([l,r]\)区间中的最长“完美序列”
\(pre\)的值是一个非递减的序列 那么可以考虑二分 二分出最靠前的 满足\(pre[i]\)在[l,r]区间内的下标 \(pos\)
那么此时区间\([l,pos-1]\)的\(pre[i]\)值都在\(l\)左面 而\([pos,r]\)区间内的\(pre[i]\)都在\([l,r]\)区间内
这样就把区间分成了两部分 那么答案就是$max ( pos - l , 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】矩阵最值
[题目描述]
我们有一个\(n\)行\(m\)列的矩阵,现在小Q有\(k\)个问题,每次询问一个以\((x_1,y_1)\)为左上角,\((x_2,y_2)\)为右下角的子矩阵的最大值。
[输入格式]
第一行三个整数\(n,m,k\)。
接下来\(n\)行,每行有\(m\)个整数,设\(a_{i.j}\)为矩阵\(i\)行\(j\)列的数字。
接下来\(k\)行,每行\(4\)个整数\(x_1,y_1,x_2,y_2\)
[输出格式]
共\(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;
}

浙公网安备 33010602011771号