ST表ヾ(×× ) ツ

ST表ヾ(×× ) ツ

一、ST表

ST表示用来解决RMQ问题的一种算法,感觉解释起来还是比较复杂,有很多大佬写得都很好,大家可以找一下,我这里简单说一下。

RMQ问题即区间最值问题,即我们要求任意区间范围内的最值,ST表的思想是,求出区间长度为 2^n(n >= 0)的区间内的最值,当我们要找某个区间的最值的时候,就用这些区间拼接起来表示比如说某个长度为3的区间的最值,就可以通过比较对应的两个长度为2且相交的区间的最值来得出。

核心代码

  for(int j=1;j<=LC;j++)
        for (int i=1;i<=n-(1<<j)+1;i++)
    		f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);

这里要注意范围, 2 ^ j <= 数列长度,接下来看一道模板题,理清一下思路

题目背景

这是一道ST表经典题——静态区间最大值

请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为O(1)。若使用更高时间复杂度算法不保证能通过。

如果您认为您的代码时间复杂度正确但是 TLE,可以尝试使用快速读入:

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

题目描述

给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。

输入格式

第一行包含两个整数 N,M ,分别表示数列的长度和询问的个数。

第二行包含 N 个整数(记为 a**i),依次表示数列的第 ii 项。

接下来 MM行,每行包含两个整数 li,ri,表示查询的区间为 [ l_i, r_i][l**i,r**i]

输出格式

输出包含 M行,每行一个整数,依次表示每一次询问的结果。

输入输出样例

输入 #1

8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8

输出 #1

9
9
7
7
9
8
7
9

说明/提示

对于30%的数据,满足: 1≤N,M≤10

对于70%的数据,满足: 1≤N,M≤10^5

对于100%的数据,满足: 1≤N≤105,1≤*M*≤2×106,ai∈[0,10^9],1≤liriN

这道题数据还是挺严的我感觉,我用了加速之后的cin cout过不了,又换了速读还是不行,最后把数组开小了点然后把cout换成printf才过。

AC code

#include <iostream>
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int f[100009][100];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main()
{
    ios::sync_with_stdio(false);
    int n, m;
    n = read();
    m = read();
    for(int i = 1 ; i <= n; i++){
        f[i][0] = read();
    }
    int l = log(n)/log(2);
    for(int j = 1; j <= l; j++){
        for(int i = 1; i <= n - (1 << j) + 1; i++){
            f[i][j] = max(f[i][j-1],f[i + (1<<(j-1))][j-1]);
        }
    }
    for(int i = 1; i <= m; i++){
        int x,y;
        x = read();
        y = read();
        l = log(y - x + 1) / log(2);
        printf("%d\n",max(f[x][l],f[y - (1<<l) + 1][l]));
    }
    return 0;
}

然后我们正式开始吧ヾ(o◕∀◕)ノヾ如果还是没有特别理解ST表的话,可以找大佬们的再看看,我下面主要给大家带来一些题解

二、go go go

1、质量检测

题目描述

为了检测生产流水线上总共 N 件产品的质量,我们首先给每一件产品打一个分数 A 表示其品质,然后统计前 M 件产品中质量最差的产品的分值 Q[m]=min{A1,A2,…Am},以及第 2 至第 M+1 件Q[m+1],Q[m+2]… 最后统计第 NM+1 至第 N 件的 Q[n]。根据 Q 再做进一步评估。

请你尽快求出 Q 序列。

输入格式

输入共两行。

第一行共两个数 NM,由空格隔开。含义如前述。

第二行共 N 个数,表示 N 件产品的质量。

输出格式

输出共 NM+1 行。

第 1 至 NM+1 行每行一个数,第 i 行的数 Q[i+M−1]。含义如前述。

输入输出样例

输入 #1

10 4
16 5 6 9 5 13 14 20 8 12

输出 #1

5
5
5
5
5
8
8
说明/提示

[数据范围]

30%的数据,N≤1000

100%的数据,N≤100000

100%的数据,MN,A≤1000000

按照刚刚的模板,我们先建立ST表,然后再利用循环输出数据即可,基本上完全和模板一样

AC code
#include <iostream>
#include <bits/stdc++.h>
//#pragma GCC optimize(2)
#define r read()
using namespace std;
int f[1000005][25];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    n = r;
    m = r;
    for(int i = 1; i <= n; i++){
        f[i][0] = r;
    }
    int l = log(n) / log(2);
    for(int j = 1; j <= l; j++){
        for(int i = 1; i<= n - (1 << j) + 1; i++){
            f[i][j] = min(f[i][j-1], f[i+(1<<(j-1))][j-1]);
        }
    }
    l = log(m)/log(2);
    for(int i = 1;i <= n - m + 1; i++){
        printf("%d\n",min(f[i][l],f[i + m - (1<<l)][l]));
    }
    return 0;
}

2、忠诚

题目描述

老管家是一个聪明能干的人。他为财主工作了整整10年,财主为了让自已账目更加清楚。要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意。但是由于一些人的挑拨,财主还是对管家产生了怀疑。于是他决定用一种特别的方法来判断管家的忠诚,他把每次的账目按1,2,3…编号,然后不定时的问管家问题,问题是这样的:在a到b号账中最少的一笔是多少?为了让管家没时间作假他总是一次问多个问题。

输入格式

输入中第一行有两个数m,n表示有m(m<=100000)笔账,n表示有n个问题,n<=100000。

第二行为m个数,分别是账目的钱数

后面n行分别是n个问题,每行有2个数字说明开始结束的账目编号。

输出格式

输出文件中为每个问题的答案。具体查看样例。

输入输出样例

输入 #1

10 3
1 2 3 4 5 6 7 8 9 10
2 7
3 9
1 10

输出 #1

2 3 1

同样是比较明显的ST表,直接上代码

AC code

#include <iostream>
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define r read()
using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
//并查集
int f[1];
int find(int k){
    if(f[k] == k){
        return k;
    }
    return f[k] = find(f[k]);
}
int s[100009][30];
int main()
{
    ios::sync_with_stdio(false);
    int m, n;
    m = r;
    n = r;
    for(int i = 1; i <= m; i++){
        s[i][0] = r;
    }
    int l = log(m)/log(2);
    for(int j = 1; j <= l; j++){
        for(int i = 1; i <= m + 1 - (1 << j); i++){
            s[i][j] = min(s[i][j-1],s[i + (1<<(j-1))][j-1]);
        }
    }
    for(int i = 1; i <= n; i++){
        int x = r;
        int y = r;
        l = log(y-x+1)/log(2);
        printf("%d",min(s[x][l],s[y + 1 - (1 << l)][l]));
        if(i != n){
        printf(" ");
        }
    }
    return 0;
}

3、[USACO07JAN]Balanced Lineup G

题目描述

For the daily milking, Farmer John’s N cows (1 ≤ N ≤ 50,000) always line up in the same order. One day Farmer John decides to organize a game of Ultimate Frisbee with some of the cows. To keep things simple, he will take a contiguous range of cows from the milking lineup to play the game. However, for all the cows to have fun they should not differ too much in height.

Farmer John has made a list of Q (1 ≤ Q ≤ 180,000) potential groups of cows and their heights (1 ≤ height ≤ 1,000,000). For each group, he wants your help to determine the difference in height between the shortest and the tallest cow in the group.

每天,农夫 John 的 n*(1≤n≤5×10^4) 头牛总是按同一序列排队。

有一天, John 决定让一些牛们玩一场飞盘比赛。他准备找一群在对列中为置连续的牛来进行比赛。但是为了避免水平悬殊,牛的身高不应该相差太大。John 准备了 q(1≤q≤1.8×10^5) 个可能的牛的选择和所有牛的身高 hi(1≤hi≤10^6,1≤in)。他想知道每一组里面最高和最低的牛的身高差。

输入格式

Line 1: Two space-separated integers, N and Q.

Lines 2…N+1: Line i+1 contains a single integer that is the height of cow i

Lines N+2…N+Q+1: Two integers A and B (1 ≤ A ≤ B ≤ N), representing the range of cows from A to B inclusive.

第一行两个数 n,q

接下来 n 行,每行一个数 hi

再接下来 q 行,每行两个整数 ab,表示询问第 a 头牛到第 b 头牛里的最高和最低的牛的身高差。

输出格式

Lines 1…Q: Each line contains a single integer that is a response to a reply and indicates the difference in height between the tallest and shortest cow in the range.

输出共 q 行,对于每一组询问,输出每一组中最高和最低的牛的身高差。

输入输出样例

输入 #1

6 3
1
7
3
4
2
5
1 5
4 6
2 2

输出 #1

6
3
0

这道题还是套模板,用两个ST表分别存最大值最小值,最后合并,通过不断练习,不断熟练。

AC code
#include <iostream>
#include <bits/stdc++.h>
#pragma GCC optimize(2)
#define r read()
using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
//并查集
int f[1000];
int find(int k){
    if(f[k] == k){
        return k;
    }
    return f[k] = find(f[k]);
}
int s1[50005][30];
int s2[50005][30];
int main()
{
    ios::sync_with_stdio(false);
    int n = r;
    int q = r;
    for(int i = 1; i <= n; i++){
        s1[i][0] = r;
        s2[i][0] = s1[i][0];
    }
    int l = log(n)/log(2);
    for(int j = 1; j <= l; j++){
        for(int i = 1; i <= n - (1 << j) + 1; i++){
            s1[i][j] = max(s1[i][j-1],s1[i + (1 << (j - 1))][j-1]);
            s2[i][j] = min(s2[i][j-1],s2[i + (1 << (j - 1))][j-1]);
        }
    }
    for(int i = 1; i <= q; i++){
        int x = r;
        int y = r;
        int l = log(y - x + 1) / log(2);
        int t1 = max(s1[x][l],s1[y + 1 - (1 << l)][l]);
        int t2 = min(s2[x][l],s2[y + 1 - (1 << l)][l]);
        printf("%d\n",t1-t2);
    }
    return 0;
}

4、 [JRKSJ R1] JFCA

题目背景

不是吧不是吧都 1202 年了还有人趁别人不在动他电脑吗?

还真有

谷民们将其称为 JFCA 或简写 JC

题目描述

给出一个环,上面有 n 个点,每个相邻的点对之间的距离为 1。

每个点有两个属性 aibi,对于点 i,定义 fi 为它与满足 ajbi 的最近的点对在环上劣弧的长度,其中 i\=j。如果没有满足条件的 j,其 fi=−1.

输入格式

输入共 3 行。
第 1 行 1 个整数 n
第 2 行 n 个整数,其中第 i 个表示 ai​,意义同上。
第 3 行 n 个整数,其中第 i 个表示 bi​,意义同上。

输出格式

输出 1 行 n 个整数,其中第 i 个表示 fi,意义同上。

输入输出样例

输入 #1

3
1 2 3
3 2 1

输出 #1

1 1 1

输入 #2

5
5 4 3 5 6
7 6 5 4 3

输出 #2

-1 2 1 1 1

输入 #3

5
1 1 2 1 1
2 2 2 2 2

输出 #3

2 1 -1 1 2
说明/提示

对于 10pts 的数据,1⩽n⩽10^2.
对于 20pts 的数据,1⩽n⩽10^3.
对于 100% 的数据,1⩽n⩽105,1⩽*ai*​,*bi*​⩽109.

我们对于测试点 4 至 11 采用捆绑测试。

样例1解释

对于 i=1,a3=3⩾b1=3, 1 和 3 的距离是 1,所以 f1=1。
对于 i=2,a3​=3⩾b2​=2, 2 和 3 的距离是 1,所以 f2​=1。
对于 i=3,a2​=2⩾b3​=1, 2 和 3 的距离是 1,所以 f3​=1。

upd2021.2.28:出题人良心地加了一组出题时发现的够强度的样例

这道题对st表的使用更加综合,是二分加st表,这需要我们不再把st表看作模板,而是更加灵活的利用这个工具,整体思路就是对每个点利用二分和st表找出周围最大的比他小的数,同时注意到这是个环形结构,所以需要断环成链

AC code
#include <bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define ull unsigned long long
#define r read()
using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
//并查集
int f[1];
int found(int k){
    if(f[k] == k){
        return k;
    }
    return f[k] = found(f[k]);
}
//辗转相除法
int gcd(int p,int q){
  int t = p % q;
  return t==0?q:gcd(q,t);
}
//阶乘
int fac(int k){
    int ans = 1;
    for(int i = 1; i<= k; i++){
        ans *= i;
    }
    return ans;
}
const int N = 3e5 + 10;
int st[N][30];
int lg[N];
int n;
int seekST(int le, int ri){
    int len = ri - le + 1;
    int q = lg[len];
    return max(st[le][q],st[ri - (1 << q) + 1][q]);
}
int seek(int x,int index){
    int le = 1;
    int ri = n;
    while(le < ri){
        int mid = (le + ri) >> 1;
        if(max(seekST(index - mid,index-1),seekST(index+1,index+mid)) >= x){
            ri = mid;
        }
        else{
            le = mid + 1;
        }
    }
    if(le == n){
        return -1;
    }
    else{
        return le;
    }
}
int main()
{
    ios::sync_with_stdio(false);
    n = r;

    //初始化st表和对数
    for(int i = 1; i <= n; i++){
        int temp = r;
        st[i + n][0] = st[i + n + n][0]= st[i][0] = temp;
    }
    for(int i = 2; i <= n * 3; i++){
        lg[i] = lg[i >> 1] + 1;
    }
    int ln = lg[n + n + n];
    for(int i = 1; i <= ln; i++){
        for(int j = 1; j + (1 << (i - 1)) - 1<= n * 3; j++){
            st[j][i] = max(st[j][i-1],st[j+(1 << (i - 1))][i-1]);
        }
    }
    for(int i = 1; i <= n; i++){
        int x = r;
        cout<<seek(x,i + n);
        if(i != n){
            cout<<" ";
        }
    }
    return 0;
}

5、[JSOI2008]最大数

题目描述

现在请求你维护一个数列,要求提供以下两种操作:

1、 查询操作。

语法:Q L

功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值。

限制:L不超过当前数列的长度。(L>0)

2、 插入操作。

语法:A n

功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。

限制:n是整数(可能为负数)并且在长整范围内。

注意:初始时数列是空的,没有一个数。

输入格式

第一行两个整数,MD,其中 M 表示操作的个数,D 如上文中所述。

接下来的 M 行,每行一个字符串,描述一个具体的操作。语法如上文所述。

输出格式

对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。

输入输出样例

输入 #1

5 100
A 96
Q 1
A 97
Q 1
Q 2

输出 #1

96
93
96
说明/提示
数据规模与约定

对于全部的测试点,保证 1≤M≤2×105,1≤*D*≤2×109。

逆序st表

AC code
#include <iostream>
#include <bits/stdc++.h>

#define r read()
using namespace std;
//速读
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int N = 2e5 + 5;
int m, d;
int a[N];
int st[N][30];
int tot = 0;
int ln[N];
int ans = 0;
void add(int x){
    st[x][0] = a[x];
    for(int i = 1; x - (1 << i) >= 0; i++){
        st[x][i] = max(st[x][i-1], st[x - (1<<(i-1))][i - 1]);
    }
}
void query(int x, int y){
    int l = ln[y - x + 1];
    ans = max(st[y][l], st[x + (1 << l) - 1][l]);
    cout<<ans<<endl;
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> m >>d;
    for(int i = 2; i <= m; i++){
        ln[i] = ln[i >> 1] + 1;
    }
    for(int i = 1; i <= m; i++){
        char c;
        cin >> c;
        if(c == 'A'){
            int n;
            cin >> n;
            a[++tot] = (n + ans) % d;
            add(tot);
        }else{
            int L;
            cin >> L;
            if(L == 1){
                ans = a[tot];
                cout<<a[tot]<<endl;
                continue;
            }
            query(tot - L + 1, tot);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值