zoukankan      html  css  js  c++  java
  • G.Greater and Greater

    G.Greater and Greater - 2020牛客暑期多校训练营(第二场) (位运算,二分查找)

    截屏2020-07-15 上午9.31.36

    题目下载:https://files.cnblogs.com/files/popodynasty/G.Greater_and_Greater.pdf.zip

    题目大意

    给定两个数列A、B,其中数列A的长度一定不小于数列B的长度。求数列A中有多少这样的连续子数列,其数列长度和B数列相同,且子数列中的各个元素均不小于B数列中对应位置的元素。

    题解

    截屏2020-07-15 上午9.52.28

    思路

    这题无法避免将A中的各个元素与B中的各个元素比较大小。

    我们可以对A中的每一个元素(A_i)求一个长度为m的bitset (S_i),其中(S_i[j] = 1 iff A_i geq B_j)

    如样例

    A: 1 4 2 8 5 7

    B: 2 3 3

    我们得到的bitset就是:

    0 0 0 / 1 1 1 / 1 0 0 / 1 1 1 / 1 1 1 / 1 1 1

    截屏2020-07-15 上午9.58.06

    将这些bitset这样排列,每一个长度为3的列全为1,则对应一个有效答案。

    思考:

    • 为什么要交错排列?
    • 为什么长度为3的列全都是1就对应着有效答案?
    • 为什么要将第一个元素(1)的bitset放右下方,将最后一个元素(7)的bitset放最左上方,如果用相反的顺序有什么逻辑错误?

    接下来遇到的问题是,如果直接用for循环比较,复杂度为O( n*m ).需要换一种更高效的方法来比较。

    看样例数据:

    A: 1 4 2 8 5 7

    B: 2 3 3

    符合题意的子数列为2 8 58 5 7

    其实样例数据等价于:

    A: 1 3 2 3 3 3

    B: 2 3 3

    符合题意的子数列为 2 3 33 3 3

    可以发现的是,对A数列中的每一个元素(alpha),我们都可以把它缩小成B数列中的一个元素(eta),这个元素(eta)是数列B中最接近(alpha)且不超过(alpha)的元素。(如果元素(alpha)比B数列的每一个元素都要小,那么保持不变即可)。这样做不会影响原答案(而且找到的每一个子数列的位置也不变)

    这样做的意义在于,可以将O(m*n)的比较过程,转化为O(n* logm) 为什么是log,先往下看后面会讲

    如此一来,我们只需要将B中的所有元素与B数列求一个大小关系即可。

    我们将B数列排序(排序的时候还要标记原位置)

    #define MaxB 20
    typedef pair<int,int> pii;
    int temp;
    int m; // B数列长度
    vector<int> B(1); // B数列,实现加一个占位
    vector<pii> sorted_B; 
    // vector 用于存储排序后的B数列,用二元组的意义:first存储数值,second存储其在原B数列的位置
    
    sorted_B.push_back(make_pair(0,0)); 
    // 加入一个(0,0)既可以占位,又可以提供一个最小值方便之后二分查找
    
    
    cin >> m;
    for(int i = 1; i <= m; i++){
      cin >> temp;
      B.push_back(temp);
      sorted_B.push_back(make_pair(temp,i));
    }
    
    sort(sorted_B.begin(),sorted_B.end()); // 默认按偏序的字典序排序
    

    然后求出对排序后的B数组求对应的bitset

    排序的意义:

    • 方便之后求A数列的bitset时做二分查找
    • 排序后的B数组相邻的元素的bitset存在递推关系

    如何得到的这个递推关系?

    举个例子

    B: 2 3 4 5 4

    sorted_B: 2 3 4 4 5

    假如我刚刚求完了元素3的bitset,为 1 1 0 0 0,现在要求元素4的bitset,由于43的相邻元素且更大,所有3的bitset为1的位,在4的bitset中也为1;在此基础上,4的bitset在所有B数值为4的位置也要设置为1.

    因此有

        for (int i = 1; i <= m; ) {
            B_bitset[i] = B_bitset[i-1];
            int j = i;
            while (j <= m) {
                if(sorted_B[i].first == sorted_B[j].first){
                    B_bitset[i].set(sorted_B[j].second);
                    j++;
                }else break;
            }
    

    后面所有的4的bitset全部都和现在求出的这个4的bitset相同。

    因此有

            for (int k = i+1; k < j; k++) {
                B_bitset[k] = B_bitset[i];
            }
    
    bitset<MaxB> B_bitset
    void get_B_bitset(){ 
      // 这个函数能够得到标准的bitset,但是没有必要;可以再二分查找上做文章.
      // 这里还是写一个得到标准bitset的函数,略微慢一些。如果想要快一点,可以看文末参考博客的代码
        for (int i = 1; i <= m; ) {
            B_bitset[i] = B_bitset[i-1];
            int j = i;
            while (j <= m) {
                if(sorted_B[i].first == sorted_B[j].first){
                    B_bitset[i].set(sorted_B[j].second);
                    j++;
                }else break;
            }
            for (int k = i+1; k < j; k++) {
                B_bitset[k] = B_bitset[i];
            }
            i = j;
        }
    }
    

    现在我们已经得到了sorted_B的bitset,利用它我们可以得到A的bitset。方法就是利用二分。

    (一些常见的二分模板: https://zhuanlan.zhihu.com/p/42185010)

    假如 B 是 3 5 7 ,如果要求 A 有一位的值为 6 ,那么 6 所对应的 bitset 的值和 B 中 5 的 bitset的值一样 (均为110),这样就能 log 得到 A 的bitset值。

    int find_pos(int val){ // 在 sorted_B 中找到最接近val的、<= val的元素(如果出现val比所有B中元素都小,搜索到0)
        int head = 0,tail = m;
        while (head < tail) {
            int mid = (head + tail) / 2;
            if(sorted_B[mid].first > val){
                tail = mid - 1;
            }else if(sorted_B[mid].first < val){
                head = mid + 1;
            }else{
                head = mid;
                break;
            }
        }
        while (head > 0) {
            if(sorted_B[head].first > val)
                head--;
            else break;
        }
        return sorted_B[head].second;
    }
    

    现在我们已经可以O(logm)求出A中任意元素的bitset (S_i)了,但不需要全部求出来并存起来(可能也存不下),只需要滚动即可。

    • 在解决了(S_i)后要考虑如何得到答案,我们发现(S_i)(S_{i-1})相比,只需要将(S_{i-1})右移一位再进行 操作,就能把两者相联系起来,那么我们只要维护一个从起始开始的 的和 A_bitset,如果 A_bitset[1]=1那么就对答案有贡献。
    • 注意要先处理好 A_bitset 中后m−1 项,而且在 A_bitset 的每次右移过程中需要将 A_bitset[m]赋值为 1。不然会影响到之后的判断。
        bitset<MaxB> A_bitset;
        int ans = 0;
        for (int i = n; i >= n - m + 1; i--) {
            A_bitset >>= 1;
            A_bitset[m] = 1;
            A_bitset &= B_bitset[find_pos(A[i])];
        }
        
        for (int i = n - m + 1; i >= 1; i--) {
            A_bitset >>= 1;
            A_bitset[m] = 1;
            A_bitset &= B_bitset[find_pos(A[i])];
            ans += A_bitset[1];
        }
        
        cout << ans;
    

    完整代码(还没提交检验,过了一些自己设计的数据):

    #include <iostream>
    #include <vector>
    #include <bitset>
    #include <algorithm>
    #include <cstdio>
    
    #define MaxA 150000
    #define MaxB 10
    using namespace std;
    
    typedef pair<int,int> pii;
    
    int n;
    int m;
    
    vector<pii> sorted_B;
    vector<int> A(1);
    vector<int> B(1);
    
    
    bitset<MaxB> B_bitset[MaxB];
    
    void init(){
        int temp;
        cin >> n >> m;
        for (int i = 1; i <= n; i++) {
            cin >> temp;
            A.push_back(temp);
        }
    
        sorted_B.push_back(make_pair(0,0));
        for (int i = 1; i <= m; i++) {
            cin >> temp;
            B.push_back(temp);
            sorted_B.push_back(make_pair(temp,i));
        }
        sort(sorted_B.begin(),sorted_B.end());
    }
    
    void get_B_bitset(){ 
      // 这个函数能够得到标准的bitset,但是没有必要;可以再二分查找上做文章.
      // 这里还是写一个得到标准bitset的函数,略微慢一些。如果想要快一点,可以看文末参考博客的代码
        for (int i = 1; i <= m; ) {
            B_bitset[i] = B_bitset[i-1];
            int j = i;
            while (j <= m) {
                if(sorted_B[i].first == sorted_B[j].first){
                    B_bitset[i].set(sorted_B[j].second);
                    j++;
                }else break;
            }
            for (int k = i+1; k < j; k++) {
                B_bitset[k] = B_bitset[i];
            }
            i = j;
        }
    }
    int find_pos(int val){ // 在 B_val_to_pos 中找到最接近val的、<= val的元素(如果出现val比所有B中元素都小,搜索到0)
        int head = 0,tail = m;
        while (head < tail) {
            int mid = (head + tail) / 2;
            if(sorted_B[mid].first > val){
                tail = mid - 1;
            }else if(sorted_B[mid].first < val){
                head = mid + 1;
            }else{
                head = mid;
                break;
            }
        }
        while (head > 0) {
            if(sorted_B[head].first > val)
                head--;
            else break;
        }
        return sorted_B[head].second;
    }
    int main(){
        init();
        get_B_bitset();
        bitset<MaxB> A_bitset;
        int ans = 0;
        for (int i = n; i >= n - m + 1; i--) {
            A_bitset >>= 1;
            A_bitset[m] = 1;
            A_bitset &= B_bitset[find_pos(A[i])];
        }
        
        for (int i = n - m + 1; i >= 1; i--) {
            A_bitset >>= 1;
            A_bitset[m] = 1;
            A_bitset &= B_bitset[find_pos(A[i])];
            ans += A_bitset[1];
        }
        
        cout << ans;
        return 0;
    }
    
    

    参考

    https://www.cnblogs.com/HexQwQ/p/13296366.html

    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    virtio前端驱动详解
    virtIO前后端notify机制详解
    virtio后端驱动详解
    centos7手动编译安装Libvirt常见问题
    手动编译安装Libvirt之后利用systemctl管理libvirtd服务
    android学习点滴一:android环境的搭建
    phpcms换域名细节
    apache配置中的小细节
    娱乐一下-加班的不归路
    qt5.3+vs2013乱码
  • 原文地址:https://www.cnblogs.com/popodynasty/p/13297537.html
Copyright © 2011-2022 走看看