离散化
离散化的作用就是将乱序并且数据差异很大的值给重新进行维护,使得它们原本的大小关系不变。
举个例子:
原数据:。
离散化:。
1. 排序(sort())。
2. 去重(unique())。
3. 标记。
数据数量不大,但数字本生很大,而且我们并不关心数字本身是谁,只关心数字大小关系。
:
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;
}
离散化+差分。
例子:
给你 个不同的区间,问线段上被覆盖次数最多的次数是多少。
l[i], r[i]。
。
。
时间复杂度:。
:
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);
}
例题一:园艺
说明
这几天徐老师对培养盆栽很感兴趣,于是他想搭建一个温室来满足他的研究欲望。
徐老师将所有的 株盆栽都放在新建的温室里,所以所有盆栽都处于完全相同的环境中。
每一株盆栽都有一个最佳生长温度区间 ,在这个范围的温度下生长会生长得最好,但是不一定会提供最佳的研究价值(徐老师认为研究发育不良的盆栽也是很有研究价值的)。
徐老师进行了若干次试验,发现若第 株盆栽的生长温度适宜,可以提供
的研究价值;若生长温度超过了适宜温度的上限,能提供
的研究价值;若生长温度低于适宜温度的下限,则能提供
的研究价值。
现在通过试验,徐老师已经得知了每一株盆栽的适宜生长温度范围,也知道了它们的 的值。你需要根据这些信息,给温室选定一个温度(这个温度可以是任意实数),使得徐老师能获得的研究价值最大。
输入格式
第一行输入一个整数 (
) ,表示盆栽数量。
接下来 行,每行五个整数
,其中(
) ,意义如上所述。
输出格式
输出一个整数,表示徐老师能获得的研究价值最大。
样例输入
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. 温度可以是实数。
:
#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;
}
例题二:等式
说明
一天徐老师在解一个方程,方程如下:
其中 得取值范围是
但不包括
。
其中 的取值范围是
且不为
的整数。
请计算这个方程有多少组整数解。
输入格式
输入一行,包含四个整数 ,中间以空格隔开。
输出格式
输出一个整数表示满足条件,方程解得数目。
样例输入
1 2 3 -4
样例输出
39088
思路
分开查找即可。 数组表示正数,
数组表示负数
。:
#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;
}
哈希(
)
哈希表,散列表。
中的
。
的底层实现是红黑树,所有操作的复杂度均为 $logn$,存储后的结点都是有序的。
_
的底层实现是
表,它的操作复杂度是不稳定的,最终速度会退化到
,可以证明速度基本是常数几倍的。
和
_
的优缺点
是最常使用的,因为它的速度很稳定。
_
速度不稳定,查询非常多,其他操作几乎没有的情况下,建表比价慢。
。
线性探测法:
表示
这个哈希值对应的数字是谁。
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;
}
字符串哈希
压缩成三进制数字。
取出字符串中间的某一段进行操作
的方式就是进制转换。
码最多只到
。
。
看成是 进制的数字。
如果只有字母的字符串 。
。
如果是一个没有限制的字符串 。
。
用算出来的 long long 类型的 。
unsigned long long 无符号的 long long。
溢出的时候相当于进行了一次取模 。
。
字符串中子串出现次数。
对于 字符串,处理出
前缀。
。
。
。
。
。
。
。
某一段的哈希值: