区间和(整数的离散化):
使用条件:
1.数值范围过大:0~1e9(称呼为数_1)
2.数的个数较少
则需将数_1(n个)映射到从0~n-1的自然数上(将数_1存入数组)
如:a[1,10,200,3000,500000]共5个数,将其映射到从0开始的5个数,该过程被称为离散化
b 0,1,,,2,,,,,,,,3,,,,,,,,,,4
问题:
-
a[]中可能有重复元素 => 去重(a有序)
-
如何算出x离散化后的值 => 二分
去重:
vector<int> alls;
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());//去重
解释:
unique(a.begin(),a.end()) 会将a数组中的重复元素移到a数组的最后面,并返回非多余元素的最后一个坐标
erase(a.begin(),a.end()) 会将a数组从begin,到end的元素删除 => 删除多余元素
练习及代码:
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int m, n;
int a[N], s[N];//a数组在数轴x的对应数组下标上储存c值;s数组记录a数组的各个前缀和
vector<int> alls;//alls储存有数的坐标x;储存需要询问的位点l,r;共n+2m个坐标点位=>300010
vector<PII> add, query;//add记录第x位数加c;query储存询问的左右区间;
int find(int x)//找到坐标点位x(数轴)在点位储存集alls中的第几个(数组坐标);数轴坐标通过find()函数得到它在alls(a/s)数组的下标(-1)
{
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;//映射到从1开始的自然数,返回其对应的数组下标加1
}
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int x, c;
cin >> x >> c;
add.push_back({ x,c });
alls.push_back(x);
}
for (int i = 0; i < m; i++)
{
int l, r;
cin >> l >> r;
query.push_back({ l,r });
alls.push_back(l);
alls.push_back(r);
}
//去重
sort(alls.begin(), alls.end());
alls.erase(unique(alls.begin(), alls.end()), alls.end());
//处理插入
for (auto item : add)//auto i = l;用于自动识别类型;for (auto item : add):依次取add中的元素(复制),赋值给item
{
int x = find(item.first);//item.first取add中的坐标x;find(x)
a[x] += item.second;//在a中对应的数组下标上储存c的值
}
//预处理前缀和
for (int i = 1; i <= alls.size(); i++)
{
s[i] = s[i - 1] + a[i];//将各个下标的前缀和记录在s数组中
}
//处理询问
for (auto item : query)//依次取出需要询问的左右边界
{
int l = find(item.first), r = find(item.second);
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
区间合并:
问题描述:803. 区间合并 - AcWing题库
给定 n个区间 [li,ri],要求合并所有有交集的区间。
注意如果在端点处相交,也算有交集。
输出合并完成后的区间个数。
例如:[1,3][1,3] 和 [2,6][2,6] 可以合并为一个区间 [1,6][1,6]。
方法:
-
所有区间按区间的左端点排序
-
扫描所有区间,并且将肯有交集的区间进行合并
代码:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
typedef pair<int, int> PII;
int n;
vector<PII> q;
void merge(vector<PII>& q)
{
vector<PII> res;
sort(q.begin(), q.end());
int st = -2e9, ed = -2e9;
for (auto x : q)
{
if (ed < x.first)//不能合并,需更新时进入
{
if (st != -2e9) res.push_back({ st,ed });//有区间时将区间存入res中,无区间时直接更新
st = x.first, ed = x.second;//更新st和ed
}
else ed = max(ed, x.second);//可以合并时,更新ed
}
if (st != -2e9) res.push_back({ st,ed });//将最后一个区间存入res中
q = res;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
int l, r;
cin >> l >> r;
q.push_back({ l,r });
}
merge(q);
cout << q.size() << endl;
return 0;
}
单链表:
概念:
如上图:单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。
通常是用结构体加指针的形式来使用单链表,但开辟一个新空间耗时太长,于是使用数组来模拟单链表的用法:
如上图,定义两个数组 e[N] en[N],e[N]存数,en[[N]存地址(坐标)
练习:
代码:
#include<iostream>
using namespace std;
const int N = 100010;
//head表示头结点的下标,e存数,ne存下标
//e[i]表示i结点的值
//ne[i]表示结点i的next指针是多少
//idx储存当前用到了那个地址(下标);当前可以用的最新的下标
int head, e[N], ne[N],idx;
//初始化
void init()
{
head = -1;
idx = 0;
}
//在头结点插入x
void add_to_head(int x)
{
e[idx] = x;
ne[idx] = head;
head = idx;
idx++;
}
//将x插到下标是k的位置的后面(第几个输入的数与idx的下标-1对应)
void add(int k, int x)
{
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx++;
}
//将下标是k的后面的点删掉
void remove(int k)
{
ne[k] = ne[ne[k]];
}
int main()
{
int m;
cin >> m;
init();//初始化
while (m--)
{
int k, x;
char op;//option
cin >> op;
if (op == 'H')
{
cin >> x;
add_to_head(x);
}
else if (op == 'D')
{
cin >> k;
if (k == 0) head = ne[head];//删除头结点
else remove(k - 1);//第k个数,存时从下标为0开始存,因此减1
}
else if (op == 'I')
{
cin >> k >> x;
add(k - 1, x);
}
}
for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';//注意如何遍历链表
cout << endl;
return 0;
}