离散化和哈希

离散化

离散化的作用就是将乱序并且数据差异很大的值给重新进行维护,使得它们原本的大小关系不变。

举个例子:

原数据:a={1,999999,50,50000,2000}

离散化:b={1,5,2,4,3}

1. 排序(sort())。

2. 去重(unique())。

3. 标记。

数据数量不大,但数字本生很大,而且我们并不关心数字本身是谁,只关心数字大小关系。

code

map<int, int> mp;
int real[N];

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        tmp[i] = a[i]; 
    } 
    sort(tmp, tmp + n);
    int len = unique(tmp, tmp + n) - tmp;
    for (int i = 0; i < len; i++) {
        mp[tmp[i]] = i + 1;
        real[i + 1] = tmp[i];
    }
    for (int i = 0; i < n; i++) {
        b[i] = mp[a[i]];
    }
    
    return 0;
}

离散化+差分。

例子:

给你 n 个不同的区间,问线段上被覆盖次数最多的次数是多少。

l[i], r[i]。

l[i], r[i] \le 10^{18}

n \le 10^5

时间复杂度:O(max(r[i]))

code

for (int i = 0; i < n; i++) {
    vis[l[i]]++;
    vis[r[i] + 1]--;
}
int sum = 0;
for (int i = 0; i < maxr; i++) {
    sum += vis[i];
    ans = max(ans, sum);
}

例题一:园艺

说明

这几天徐老师对培养盆栽很感兴趣,于是他想搭建一个温室来满足他的研究欲望。

徐老师将所有的 n 株盆栽都放在新建的温室里,所以所有盆栽都处于完全相同的环境中。

每一株盆栽都有一个最佳生长温度区间 [l,r],在这个范围的温度下生长会生长得最好,但是不一定会提供最佳的研究价值(徐老师认为研究发育不良的盆栽也是很有研究价值的)。

徐老师进行了若干次试验,发现若第 i 株盆栽的生长温度适宜,可以提供 a_i 的研究价值;若生长温度超过了适宜温度的上限,能提供 b_i 的研究价值;若生长温度低于适宜温度的下限,则能提供 c_i 的研究价值。

现在通过试验,徐老师已经得知了每一株盆栽的适宜生长温度范围,也知道了它们的 a,b,c 的值。你需要根据这些信息,给温室选定一个温度(这个温度可以是任意实数),使得徐老师能获得的研究价值最大。

输入格式

第一行输入一个整数 n(1 \le n \le 10^4)  ,表示盆栽数量。

接下来 n 行,每行五个整数 1 \le l_i, r_i, a_i, b_i, c_i \le 10^4,其中(l_i \le r_i) ,意义如上所述。

输出格式

输出一个整数,表示徐老师能获得的研究价值最大。

样例输入
5
5 8 16 20 12
10 16 3 13 13
8 11 13 1 11
7 9 6 17 5
2 11 20 8 5
样例输出
83

思路

如果让你暴力枚举所有温度作为答案的话,有几个问题。

1. 有很多不需要枚举的温度。

2. 温度可以是实数。

code

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;
ll l[N], r[N], a[N][4];
ll n, k, ans;

ll check(ll k) {
    ll sum = 0;
    for (ll i = 1; i <= n; i++) {
        if (k < l[i]) {
            sum += a[i][2];
        } else if (k >= l[i] && k < r[i]) {
            sum += a[i][0];
        } else {
            sum += a[i][1];
        }
    }
    return sum;
}

int main() {
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++) {
        scanf("%lld%lld%lld%lld%lld", &l[i], &r[i], &a[i][0], &a[i][1], &a[i][2]);
        k = max(k, r[i] + 1); 
    }
    for (ll i = 1; i <= k; i++) {
        ans = max(ans, check(i));
    }
    printf("%lld", ans);
    return 0;
}

例题二:等式

说明

一天徐老师在解一个方程,方程如下:

a \times x_1^2 + b \times x_2^2 + c \times x_3^2 + d \times x_4^2 = 0

其中 a,b,c,d 得取值范围是 [-50, 50] 但不包括 0

其中 x_i 的取值范围是 [-100, 100] 且不为 0 的整数。

请计算这个方程有多少组整数解。

输入格式

输入一行,包含四个整数 a,b,c,d,中间以空格隔开。

输出格式

输出一个整数表示满足条件,方程解得数目。

样例输入
1 2 3 -4
样例输出
39088

思路

分开查找即可。f1 数组表示正数,f2 数组表示负数


code

#include <bits/stdc++.h> 
using namespace std;

typedef long long ll;
const int N = 2e6 + 10;
int f1[N], f2[N];
int a, b, c, d, ans;

int main() {
    scanf("%d%d%d%d", &a, &b, &c, &d);
    for (int x1 = 1; x1 <= 100; x1++) {
        for (int x2 = 1; x2 <= 100; x2++) {
            int k = a * x1 * x1 + b * x2 * x2;
            if (k >= 0) {
                f1[k]++;
            } else {
                f2[-k]++;
            }
        }
    }
    int sum = 0;
    for (int x1 = 1; x1 <= 100; x1++) {
        for (int x2 = 1; x2 <= 100; x2++) {
            int k = c * x1 * x1 + d * x2 * x2;
            if (k > 0) {
                sum += f2[k];
            } else {
                sum += f1[-k];
            }
        }
    }    
    printf("%d", sum * 16); 
    return 0;
}

哈希(hash

哈希表,散列表。

STL 中的 map

map 的底层实现是红黑树,所有操作的复杂度均为 $logn$,存储后的结点都是有序的。

unordered_map 的底层实现是 hash 表,它的操作复杂度是不稳定的,最终速度会退化到 O(n),可以证明速度基本是常数几倍的。

mapunordered_map 的优缺点

map 是最常使用的,因为它的速度很稳定。

unordered_map 速度不稳定,查询非常多,其他操作几乎没有的情况下,建表比价慢。

hash \ number=x \%mod

线性探测法:

hash[i] 表示 i 这个哈希值对应的数字是谁。

int hash[10010];//初始化为 -1 

int add_hash(int x) {
    int p = x % mod;
    while (hash[p] != -1) {
        if (hash[p] == x) {
            return p;
        } 
        p++;
        if (p == mod) {
            p = 0;
        }
    }
    hash[p] = x;
    return p;
} 

int get_hash(int x) {
    int p = x % mod;
    while (hash[p] != -1) {
        if (hash[p] == x) {
            return p;
        } 
        p++;
        if (p == mod) {
            p = 0;
        }
    }
    return -1;
}

字符串哈希

0/1/2 压缩成三进制数字。

取出字符串中间的某一段进行操作

hash 的方式就是进制转换。

ASCll 码最多只到 127

0 \sim 127

看成是 127 进制的数字。

x = s_0 + s_1 \times 127 + s_2 \times 127^2 + s_3 \times 127^3 + \dots + s_n \times 127^n

如果只有字母的字符串 hash

base=31

如果是一个没有限制的字符串 hash

base=131

x = s_0 + s_1 \times 131 + s_2 \times 131^2 + s_3 \times 131^3 + \dots + s_n \times 131^n

用算出来的 long long 类型的 x \% 2^{64}

unsigned long long 无符号的 long long。

溢出的时候相当于进行了一次取模 x \% 2^{64}

unsigned\ long\ long\ x = s_0 + s_1 \times 131 + s_2 \times 131^2 + s_3 \times 131^3 + \dots + s_n \times 131^n

字符串中子串出现次数。

对于 S 字符串,处理出 H[i] 前缀。

H[i] = H[i + 1] \times 131 + s[i]

H[4] = s_4

H[3] = s_4 \times 131 + s_3

H[2] = s_4 \times 131^2 + s_3 \times 131 + s_2

H[1] = s_4 \times 131^3 + s_3 \times 131^2 + s_2 \times 131 + s_1

H[0] = s_5 \times 131^4 + s_4 \times 131^3 + s_3 \times 131^2 + s_1 \times 131 + s_0

\dots

H[i] = s_{n - 1} \times 131^{n - i - 1} + s_{n - 2} \times 131^{n - i - 2} + \dots + s_i

某一段的哈希值:
Hash[i, L] = H[i] - H[i + L] \times 131^L
Hash[i, L] = s_{n - 1}131^{n - i - 1} + s_{n - 2}131^{n - i - 2} + \dots + s_i
- (s_{n - 1}131^{n - i - 1} + s_{n - 1}131^{n - i - 2} + \dots + s_{i + L + 1}131^{L + 1} + s_{i + L}131^L)
= s_{i + L - 1}131^{L - 1} + s_{i + L - 2}131^{L - 2} + \dots + s_{i + 1}131 + s_i

  • 21
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值