zoukankan      html  css  js  c++  java
  • 离散化

    离散化是把无穷大集合中的若干个元素映射为有限集合以便于统计的方法。

    若已知a数组,我们要做的就是把它排序并去掉重复的数值,得到有序数组b[1]~b[m],在b数组的下标i与数值b[i]之间建立映射关系。

    步骤

    1.将所有需要离散化的数据(如下面例子中的下标)放到一个容器中(以下使用vector,当然可以用数组代替);
    
    2.排序,去重(可以手写,也可以用STL的algorithm库中的unique函数);
    
    3.要查询原数据在容器中的位置只需在容器中二分查找第一个大于等于该数据的数的位置即可。
    

      

    参考博客:https://www.cnblogs.com/ninedream/p/11241165.html

    多说无益,直接上代码

    一维

    区间和

    假定有一个无限长的数轴,数轴上每个坐标上的数都是0。
    现在,我们首先进行 n 次操作,每次操作将某一位置x上的数加c。
    近下来,进行 m 次询问,每个询问包含两个整数l和r,你需要求出在区间[l, r]之间的所有数的和。
    

      

    输入格式
    第一行包含两个整数n和m。
    接下来 n 行,每行包含两个整数x和c。
    再接下里 m 行,每行包含两个整数l和r。
    
    输出格式
    共m行,每行输出一个询问中所求的区间内数字和。
    
    数据范围
    −109≤x≤109,
    1≤n,m≤105,
    −109≤l≤r≤109,
    −10000≤c≤10000
    

      

    代码(模板)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+10;
    vector<int> alls;
    int n,m,x[N],c[N],l[N],r[N];
    int a[N*3],s[N*3];
    // 找到x在alls中的下标
    int find(int x){
        int l=0,r=alls.size()-1;
        while(l<r){
            int mid=l+r>>1;
            if(alls[mid]>=x)r=mid;
            else l=mid+1;
        }
        return r+1;
    }
    
    int main(){
        std::ios::sync_with_stdio(false);
        cin>>n>>m; 
        for(int i=0;i<n;i++){
            cin>>x[i]>>c[i];
            alls.push_back(x[i]);
        }
        for(int i=1;i<=m;i++){
            cin>>l[i]>>r[i];
            all.push_back(l[i]),alls.push_back(r[i]);
        }
        // 排序 + 判重
        sort(alls.begin(),alls.end());
        alls.erase(unique(alls.begin(),alls.end()),alls.end());
        // 插入操作
        for(int i=0;i<n;i++)a[find(x[i])]+=c[i];
        // 预处理前缀和
        for(int i=1;i<=alls.size();i++)s[i]=s[i-1]+a[i];
        // 查询操作
        for(int i=0;i<=m;i++)cout<<s[find(r[i])]-a[find(l[i])-1]);
        
        return 0;
    }
    

      

    二维

    赶牛入圈

    农夫约翰希望为他的奶牛们建立一个畜栏。
    这些挑剔的畜生要求畜栏必须是正方形的,而且至少要包含C单位的三叶草,来当做它们的下午茶。
    畜栏的边缘必须与X,Y轴平行。
    约翰的土地里一共包含N单位的三叶草,每单位三叶草位于一个1 x 1的土地区域内,区域位置由其左下角坐标表示,并且区域左下角的X,Y坐标都为整数,范围在1到10000以内。
    多个单位的三叶草可能会位于同一个1 x 1的区域内,因为这个原因,在接下来的输入中,同一个区域坐标可能出现多次。
    只有一个区域完全位于修好的畜栏之中,才认为这个区域内的三叶草在畜栏之中。
    请你帮约翰计算一下,能包含至少C单位面积三叶草的情况下,畜栏的最小边长是多少。
    

      

    输入格式
    第一行输入两个整数 C 和 N。
    接下来 N 行,每行输入两个整数 X 和 Y,代表三叶草所在的区域的X,Y坐标。
    同一行数据用空格隔开。
    
    输出格式
    输出一个整数,代表畜栏的最小边长。
    
    数据范围
    1≤C≤500,
    C≤N≤500
    

      

     代码(模板)

    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    typedef pair<int,int> PII;
    const int maxn = 1010;
    int n,C;
    PII points[maxn];
    vector<int> numbers;
    int sum[maxn][maxn];
    int get(int x){//查找x在numbers中的位置
        int l = 0,r = numbers.size() - 1;
        while(l < r){
            int mid = l + r >> 1;
            if(numbers[mid] >= x)   r = mid;
            else    l = mid + 1;
        }
        return r;
    }
    bool check(int len){//判断是否满足条件
        for(int x1 = 0,x2 = 1;x2 < numbers.size();x2++){
            while(numbers[x2] - numbers[x1 + 1] + 1 > len)  x1++;
            for(int y1 = 0,y2 = 1;y2 < numbers.size();y2++){
                while(numbers[y2] - numbers[y1 + 1] + 1 > len)  y1++;
                if(sum[x2][y2] - sum[x1][y2] - sum[x2][y1] + sum[x1][y1] >= C)  return true;
            }
        }
        return false;
    }
    int main(){
        cin>>C>>n;
        numbers.push_back(0);//哨兵
        for(int i = 0;i < n;i++){
            int x,y;
            cin>>x>>y;
            points[i] = {x,y};
            numbers.push_back(x);
            numbers.push_back(y);
        }
        sort(numbers.begin(),numbers.end());
        numbers.erase(unique(numbers.begin(),numbers.end()),numbers.end());//离散化
        for(int i = 0;i < n;i++){
            int x = get(points[i].first),y = get(points[i].second);
            sum[x][y] ++;  
        }
        for(int i = 1;i < numbers.size();i++)
            for(int j = 1;j < numbers.size();j++)
                sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];//求前缀和
        int l = 1,r = 10000;
        while(l < r){//二分len
            int mid = l + r >> 1;
            if(check(mid))  r = mid;
            else    l = mid + 1;
        }
        cout<<r<<endl;
        return 0;
    }
    

      

  • 相关阅读:
    du熊学斐波那契I
    《博客园精华集》分类索引
    C++中指针和引用的区别
    堆和栈的区别
    getch和getchar的区别
    class和struct
    ARM开发步骤
    ARM寻址方式
    存储器映射
    思维中的错误
  • 原文地址:https://www.cnblogs.com/myhnb/p/11241928.html
Copyright © 2011-2022 走看看