2023-09-4 阴阳龙 使用set和map降低搜索时间复杂度(本质是使用了二分来优化搜索)
2023-09-4 阴阳龙 使用set和map降低搜索时间复杂度(本质是使用了二分来优化搜索)
题目重述
题目别看这么长,其实几句话就可以把题目大概讲清楚,就是先给你p个位置(是人的位置除非阴阳龙让他们旋转了,他们本身是不会动的),然后给你q个位置(是q个阴阳龙的位置,这个位置也可以看作是不动的),然后找到离每个阴阳龙的位置最近的人的位置(也就是距离最小),然后根据所在阴阳龙的位置旋转坐标,然后最后给出q次后的人的位置。
注意阴阳龙只会影响8个方向上的人。
思路
一开始我是没有优化思路的,就是直接暴力,使用 n 2 n^2 n2的复杂度去搜索,然后发现直接超时,第一个样例就过不了,我们可以发现复杂度最大的就是q里面的搜索离阴阳龙最近的人的位置比较耗时间复杂度是 n n n,但是当时没有想到合适的数据结构来,要是想到用二分搜索,然后使用set这个容器基本上这题就出来了。
其次最巧妙的就是使用了map算是一种离散化方式吧,而不是使用数组,map的索引时间复杂度是 l o g n log n logn而set的二分搜索的复杂度也是 l o g n logn logn 最终的复杂度只有 n l o g n nlogn nlogn 但是需要注意的点也有一些请看下面的介绍
map<int ,int >
本质上可以看做是一个一维整形数组,比数组的好的地方就是他的索引可以很大,而不占用那么大的连续空间,
这里我们使用了第一个int标识一条线第二个int表示存储位置
map<int , int > mp[4]
这相当于是一个二维数组, // 存储位置和对应 mp[0]行 、mp[1]列 、mp[2]斜线 、mp[3]反斜线
遇到的问题、学习到的知识
set本质是一个红黑树,他自动将元素排序,如果我们看到二分搜索需要每次重新排序的,那么我们可以用set,我们只管往里插,他会自动排好序,并且复杂度是 l o g n logn logn ,然后这题就很适合,旋转的点后需要删除后重新插入然后排序,这里set就发挥作用了,复杂度降低很多。
- 使用方法
struct point//定义结构体
{
int x, y;
int id;
point(int x, int y, int id) : x(x), y(y), id(id) {}
point() {}
} peo[100001];
// 结构体符号重载,以遍set能够排序,注意必须要重载小于号,重载大于号没用
const bool operator<(struct point a, struct point b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
map<int, int> mp[4]; // 存储位置和对应 //0行 1列 2斜线 3反斜线
vector<set<point>> points;
- 然后我按照上述思路写了以后发现仍然时间超时
原因竟然是我清楚set中的元素是使用的遍历清除
就是
set<point>::iterator it=points[pos].begin();
for(;it!=points[pos].end();it++)
{
if(it->id==id)
{
points[pos].erase(it);
}
}
这样的复杂度很高
应该使用自带的erase直接删除
points[pos].erase(people);
这样复杂度只用 l o g n log n logn
- 遇到的坑爹问题
for (int i = 0; i < len; i++)
{
if (resDis[i] == toPeo)
{
delete_mp(peo[miniDis[i].id]);
changePoint(toPeo, miniDis[i], u, v, t);
}
}
for (int i = 0; i < len; i++)
{
if (resDis[i] == toPeo)
{
insert_mp(peo[miniDis[i].id]);
}
}
注意这个delete_mp和insert_mp不能在一个循环中,因为set是会自动去重的,如果前面有的点插入到和后面的点相同的位置就被delete了,所以要全delete完才能插入
- 遇到的小问题(太蠢了)
这里u-1 v-1才对
- upper_bound 和 lower_bound 的使用
upper_bound和lower_bound 二分查找
lower_bound 返回的是第一个 大于等于 val 的位置,upper_bound 返回的是第一个大于val的位置
lower_bound(first, end, val)
upper_bound(first, end, val)
例如: 有序数组 1, 5, 9, 11, 11, 13
lower_bound(first, end, 5) - first = 1
upper_bound(first, end, 5) - frist = 2
lower_bound(first, end, 6) - first = 2
upper_bound(first, end, 6) - first = 2
lower_bound(first, end, 11) - first = 3
upper_bound(first, end, 11) - first = 5
完整代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct point
{
int x, y;
int id;
point(int x, int y, int id) : x(x), y(y), id(id) {}
point() {}
} peo[100001];
// 结构体符号重载
const bool operator<(struct point a, struct point b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
int d[8][2] = {{-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}};
map<int, int> mp[4]; // 存储位置和对应 //0行 1列 2斜线 3反斜线
vector<set<point>> points;
int n, m, p, q;
int resDis[8]; // 存储离的最近的哪些点的距离
struct point miniDis[8]; // 存储离的最近的哪些点
int len = 0; // 存储离的最近的哪些点的长度
int getDistance(int u, int v, int x1, int y1)
{
int tempd = 0;
if (x1 == u)
{
tempd = abs(y1 - v);
}
else if (y1 == v)
{
tempd = abs(x1 - u);
}
else if (abs(x1 - u) == abs(y1 - v))
{
tempd = abs(x1 - u);
}
return tempd;
}
void insert_set(int type, int key, struct point people)
{
if (mp[type].find(key) == mp[type].end() || mp[type][key] == 0)
{
set<point> temp;
temp.insert(people);
int id = points.size();
points.push_back(temp);
mp[type][key] = id;
}
else
{
int pos = mp[type][key];
points[pos].insert(people);
}
}
void delete_set(int type, int key, struct point people)
{
int pos = mp[type][key];
points[pos].erase(people);
}
void insert_mp(struct point people) // 要将 people 插入到行、列、斜线和反斜线的set中
{
insert_set(0, people.y, people);
insert_set(1, people.x, people);
insert_set(2, people.y - people.x, people);
insert_set(3, people.y + people.x, people);
}
void delete_mp(struct point people)
{
delete_set(0, people.y, people);
delete_set(1, people.x, people);
delete_set(2, people.y - people.x, people);
delete_set(3, people.y + people.x, people);
}
void searchSet(int type, int key, struct point p1)
{
int pos = mp[type][key];
if (pos == 0)
return;
// 因为一个set代表一个方向,一个方向上的最多有两个点距离最近
set<point>::iterator l = points[pos].lower_bound(p1);
set<point>::iterator r = points[pos].upper_bound(p1);
if (r != points[pos].end() && l != points[pos].begin()) // 如果左右都有数据则取最近的那一个
{
l--;
int r_dis = getDistance(p1.x, p1.y, r->x, r->y);
int l_dis = getDistance(p1.x, p1.y, l->x, l->y);
if (r_dis > l_dis)
{
miniDis[len++] = *l;
resDis[len - 1] = l_dis;
}
else if (r_dis < l_dis)
{
miniDis[len++] = *r;
resDis[len - 1] = r_dis;
}
else // 如果两个距离一样
{
miniDis[len++] = *r;
resDis[len - 1] = r_dis;
miniDis[len++] = *l;
resDis[len - 1] = l_dis;
}
}
else if (l != points[pos].begin())
{
l--;
int l_dis = getDistance(p1.x, p1.y, l->x, l->y);
miniDis[len++] = *l;
resDis[len - 1] = l_dis;
}
else if (r != points[pos].end())
{
int r_dis = getDistance(p1.x, p1.y, r->x, r->y);
miniDis[len++] = *r;
resDis[len - 1] = r_dis;
}
}
void changePoint(int k, struct point p1, int u, int v, int t)
{
int direct;
int x = p1.x, y = p1.y;
int pos = p1.id;
for (direct = 0; direct < 8; direct++)
{
if (x == u + k * d[direct][0] && y == v + k * d[direct][1])
{
break;
}
}
peo[pos].x = u + k * d[(direct + t) % 8][0];
peo[pos].y = v + k * d[(direct + t) % 8][1];
}
void solve(int u, int v, int t)
{
int toBoard = min(min(u - 1, n - u), min(v - 1, m - v));
int toPeo = -1; // 离人最近的距离
len = 0;
// 二分查找离阴阳龙最近的人的位置
// 将离得最近的那些人的指针放在一个数组中
struct point temp = point(u, v, 0);
searchSet(0, v, temp);
searchSet(1, u, temp);
searchSet(2, v - u, temp);
searchSet(3, v + u, temp);
for (int i = 0; i < len; i++)
{
if (toPeo == -1 || resDis[i] < toPeo)
{
toPeo = resDis[i];
}
}
if (toPeo > toBoard)
{
return;
}
for (int i = 0; i < len; i++)
{
if (resDis[i] == toPeo)
{
delete_mp(peo[miniDis[i].id]);
changePoint(toPeo, miniDis[i], u, v, t);
}
}
for (int i = 0; i < len; i++)
{
if (resDis[i] == toPeo)
{
insert_mp(peo[miniDis[i].id]);
}
}
len = 0;
}
int main()
{
cin >> n >> m >> p >> q;
points.push_back(set<point>());
for (int i = 1; i <= p; i++)
{
cin >> peo[i].x >> peo[i].y;
peo[i].id = i;
insert_mp(peo[i]);
}
int u, v, t;
for (int i = 1; i <= q; i++)
{
cin >> u >> v >> t;
solve(u, v, t);
}
ll sum = 0;
for (int i = 1; i <= p; i++)
{
// cout<<peo[i].x<<' '<<peo[i].y<<endl;
sum = ((ll)sum) ^ ((ll)i * (ll)peo[i].x + (ll)peo[i].y);
}
cout << sum;
return 0;
}