zoukankan      html  css  js  c++  java
  • st

    简介

    st表是解决RMQ问题(静态区间最值问题)的一种强有力的工具

    它可以做到O(nlogn)预处理,O(1)查询最值

    原因:倍增中的层数是log2(n),即2^j <= n, 所以d中元素不超过n*logn, 而每个元素都可以在常数的范围内计算完毕,所以总时间是O(nlogn)

    算法

    d(i,j)表示从 i 开始的,长度为2的 j 次方的一段元素中的最值,可用递推计算d

    找 i 的二进制数中的第一位

    d(i,j) = max/min(d(i, j-1), d(i+2^j, j-1) )

    //以最大值为例 (虽然RMQ的意思是最小值
    void st() {
    	for(int i = 1; i <= n; i++) f[i][0] = a[i];
    	for(int k = 1; (1<<k) <= n; k++) 
    		for(int i = 1; i+(1<<k)-1 <= n; i++) { //有边界
    			d[i][k] = max(d[i][k-1], d[i + (1<<(k-1))][k-1] );
    		}
    }
    
    int RMQ(int l, int R) {
    	int k = 0;//使k为满足2^k <=  R-L+1的最大整数,则以L开头, 以R结尾的两个长为2^k的区间合起来就覆盖了整个[L,R] 
    	while(( 1<<(k+1) ) <= R-L+1) k++;//如果写"(1<<k) <= R-L+1" , k还会再++,这样就可能越界了
    	return max(d[L][k], d[R-(1<<k)+1][k]); 
    }
    

    例题

    UVA-11235Frequent values

    tiyi:

    给出一个非降序的整数数组,你的任务是对于一系列询问(i,j),回答区间内出现最多的值的次数

    输入格式:

    多组数据

    每组: 第一行n,q(访问数)(1<=n,q<=100000) 。第二行n个非降序的整数(-100000<=a<=100000).以下q行,包括 i,j(合法的),输入结束标志为n==0

    分析

    因为整个数组是非降序的,所以可以用游程编码( 百度百科点这 )(其他博客点这)

    定义的数组和他们的作用相信你看了那些就懂了(或者看看刘汝佳蓝色书的P198页,那有详解)

    查询(L, R)结果为以下三部分最大值: L到L所在段的结点处的元素个数(即right[L]-L+1) , R到R所在段的开始处的元素个数(即R-left[R]+1), 中间第num[L]+1段到第num[R]-1段的count最大值

    注意特殊情况: 若L和R在同一段,


    代码:

    ps: 由于评测不了,我不知道我写的对不对,希望有人看见后能提醒下,我一定回来,谢谢

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define MAX 100000+99
    int val[MAX], countt[MAX], cnt;//分别表示第i段的数值和出现的次数 
    int num[MAX], left[MAX], right[MAX];//分别表示位置p所在边的编号,左右端点的位置 
    int a[MAX];
    int d[MAX][32];//log2(n)
    int isleft;//记录最左端的编号 
    
    int n,q;
    
    void init() {
    	for(int i = 1; i <= cnt; i++) d[i][0] = countt[i];
    	for(int k = 1; (1<<k) <= cnt; k++) 
    		for(int i = 1; i+(1<<k)-1 <= cnt; i++) 
    			d[i][k] = max(d[i][k-1], d[i + (1<<(k-1))][k-1]);
    }
    
    int RMQ(int L, int R) {
    	int k = 0;
    	while(( 1<<(k+1) ) <= R-L+1) k++;
    	return max(d[L][k], d[R-(1<<k)+1][k]);
    }
    
    int main() {
        while(scanf("%d%d",&n, &q) == 2) {
            for(int i = 1; i <= n; i++) {
                scanf("%d",&a[i]);
                if(a[i] > a[i-1]) {
                	cnt++;
                	val[cnt] = a[i];
                	countt[cnt]++;
                	num[i] = cnt;
                	left[i] = i;
                	for(int j = i-1; j >= 1 && num[j] == num[i-1]; j--) right[j] = i-1;//i-1所在的一段已完,处理right 
    				for(int j = i-1; j >= isleft && isleft != 0; j--) left[j] = isleft;//处理left 
    				isleft = i;
    			} else {
    				countt[cnt]++;
    				num[i] = cnt;
    			}
            }
    //		for(int i = 1; i <= n; i++) {
    //			printf("第%d个数所在边的编号, 左端点, 右端点: 
    ", i);
    //			printf("		%d   %d   %d
    ", num[i], left[i], right[i]);
    //		}//温馨提示: 注意差错哦 
    //		printf("当前最左端: %d  当前的段数: %d", isleft, cnt);
    		for(int i = n; i >= isleft; i--) left[i] = isleft, right[i] = n;
    //		for(int i = 1; i <= n; i++) {
    //			printf("第%d个数所在边的编号, 左端点, 右端点: 
    ", i);
    //			printf("		%d   %d   %d
    ", num[i], left[i], right[i]);
    //		}
            init();
            int L,R,ans = 0;
            for(int i = 1; i <= q; i++) {
            	scanf("%d%d",&L, &R);
            	if(num[L] == num[R]) ans = R-L+1;
          		else {
            		ans = max(R-left[R]+1, right[L]-L+1);
            		if(num[L]+1 < num[R]) ans = max(ans, RMQ(num[L]+1, num[R]-1));
         		}
         		printf("%d
    ", ans);
    		}
        }
        return 0;
    }
    

    这里还给出刘汝佳的std代码(我没仔细看....

    // UVa11235 Frequent Values
    // Rujia Liu
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    
    const int maxn = 100000 + 5;
    const int maxlog = 20;
    
    // 区间最*大*值
    struct RMQ {
      int d[maxn][maxlog];
      void init(const vector<int>& A) {
        int n = A.size();
        for(int i = 0; i < n; i++) d[i][0] = A[i];
        for(int j = 1; (1<<j) <= n; j++)
          for(int i = 0; i + (1<<j) - 1 < n; i++)
            d[i][j] = max(d[i][j-1], d[i + (1<<(j-1))][j-1]);
      }
    
      int query(int L, int R) {
        int k = 0;
        while((1<<(k+1)) <= R-L+1) k++; // 如果2^(k+1)<=R-L+1,那么k还可以加1
        return max(d[L][k], d[R-(1<<k)+1][k]);
      }
    };
    
    int a[maxn], num[maxn], left[maxn], right[maxn];
    RMQ rmq;
    int main() {
      int n, q;
      while(scanf("%d%d", &n, &q) == 2) {
        for(int i = 0; i < n; i++) scanf("%d", &a[i]);
        a[n] = a[n-1] + 1; // 哨兵
        int start = -1;
        vector<int> count;
        for(int i = 0; i <= n; i++) {
          if(i == 0 || a[i] > a[i-1]) { // 新段开始
            if(i > 0) {
              count.push_back(i - start);
              for(int j = start; j < i; j++) {
                num[j] = count.size() - 1; left[j] = start; right[j] = i-1;
              }
            }
            start = i;
          }
        }
        rmq.init(count);
        while(q--) {
          int L, R, ans;
          scanf("%d%d", &L, &R); L--; R--;
          if(num[L] == num[R]) ans = R-L+1;
          else {
            ans = max(R-left[R]+1, right[L]-L+1);
            if(num[L]+1 < num[R]) ans = max(ans, rmq.query(num[L]+1, num[R]-1));
          }
          printf("%d
    ", ans);
        }
      }
      return 0;
    }
    
  • 相关阅读:
    matlab2016b
    【ccf- csp201509-4】高速公路
    【ccf- csp201412-2】z字形扫描
    【ccf-csp201512-5】矩阵
    【动态规划】矩阵连乘分析
    JAVA环境搭建
    【离散数学2】代数系统趣题
    给文本编辑框绑定变量
    清空文本框SetDlgItemText(IDC_TXTXT,NULL);
    有关SetTimer函数的用法
  • 原文地址:https://www.cnblogs.com/tyner/p/10982036.html
Copyright © 2011-2022 走看看