平均气温(Temperature)
平均气温(Temperature)
Description
A weather station collects temperature data from observation stations all over the country every day, and provides statistical inquiry services to remote users through the Internet. One of the most common types of queries is to calculate the average temperature based on observations from all observatories in the user-specified rectangular area. As more observatories continue to build, the size of the raw data itself has ballooned. In addition, although it can be assumed that the data collected every day is relatively fixed, as the user population expands, the frequency of queries increases. In view of the fact that the efficiency of the traditional brute force algorithm can no longer meet the practical requirements, the weather station has to ask you to help, improve the efficiency of the query by improving the data structure and algorithm.
With a set of function interfaces provided by the weather station, the server can access all the collected data and report the results of the query.
Interface description
int GetNumOfStation(void);
This function must be called first, which returns the number n
of observatories.
void GetStationInfo(int no, int *x, int *y, int *temp);
This function returns information of no-th observatories: its geographic coordinates (*x, *y) and its measured temperature value *temp. The measurement accuracy of each observation station is based on 0.01 ° C, for example, 12.34 ° C is expressed as an integer of 1234.
int GetQuery(int *x1, int *y1, int *x2, int *y2);
This function receives the next query request. If it returns 1, it means this is a valid query. The four sides of the matrix area are parallel to the x or y axis, respectively. (*x1, y1) and (x2, *y2) are the coordinates of their southwest and northeast corners, respectively. An observatory that passes through a rectangular boundary is also considered to fall within it. If it returns 0, it means there are no more queries and your program can exit.
void Response(int temp);
For the current query, you can truncate the result after calculating the corresponding average temperature (for example, the output of 12.345 °C is 1234, the output of -12.345 °C is -1234), and then sent to the interface.
Pay Attention:When a query is received by GetQuery(), if the result of the query is not reported by the Response() function, an error will be output because the result of the previous query cannot be reported. That is, GetQuery() and Response() must be called alternately, n times each.
Test description
For your debugging and testing, the temperature.h and temperature_lib.c files are included with the problem. The former stipulates the above interface, the latter is an implementation of this set of interfaces - the implementation on OJ is different, but the interface is completely consistent. You can compile them with your code when you debug, but you don't have to submit them when testing. Even if you submit them, OJ will ignore them automatically.
Input
When you debug offline, the three input interfaces implemented by temperature_lib.c
will read data from file temperature.in
in current directory, so you can set different input data by changing the file temperature.in
in the following format
The first line has two integers "n, m" where n is the number of observatories and m is the number of queries.
The following n lines describe each observatory, each line contains three integers "x, y, t" where (x, y) is the coordinates of the observatory and t is the measured temperature value of the station.
The next m lines describe each query operation, each line contains four integer "x1,y1,x2,y2" where (x1,y1) represents the southwest corner and (x2,y2) represents the northeast corner.
Output
When you debug offline, the Response()
interface implemented by temperature_lib.c
will write all output results to file temperature.out
after the program runs.
Output file has n
lines, each line contains one integer, indicating the average temperature obtained per query.
If the query area doesn't contain any observatories, please output 0.
Example
Input
4 2
0 0 1000
1 1 1300
2 2 1600
3 3 1100
0 0 1 1
0 0 10 10
Output
1150
1250
Restrictions
0<=n<=50000
0<=m<=500000
The coordinates of the observatory is in [-2^31, 2^31), and the coordinates of query area satisfy x1<=x2 and y1<=y2.
Time: 10 sec
Memory: 256 MB
Hints
Please use 64-bit integer for temperature calculations to prevent overflow.
kd-tree
range tree
The rules of this problem are more complicated than other problems. If you have some issues, please ask in discussion area.
这是一种全新的数据结构,从未接触过……
题目下面提示说可以用kd树或者R树做,我就先了解了一下kd树,可能是我的kd树写的太丑,有四个点一直tle,就去看了看R树,网上关于R树的东西少之又少,就看了一篇博客,介绍R树的思想。最终还是没有学会写R树,但幸好从R树的时空复杂度得到启发,自己魔改了一下AVL树,把这个题过了(代码好像跑的还挺快……)。
- 原理与要点:kd树的就不说了,就是一层按x排序,一层按y排序,构造一个可以二维查询的树。
主要说一下我最终AC的代码。在我看了R树之后发现他比kd树查询要快(kd树根号n,R树logn),是因为R树用空间换时间,空间复杂度变为了\(O(nlogn)\),所以每次可以在logn的时间里进行二维的查询。这个\(O(nlogn)\)的空间复杂度引起了我的注意,一颗二叉树是logn层,\(O(nlogn)\)的空间复杂度也就相当于每一层都存了这n个点的数据信息。然后我就想,如果我用x坐标建一颗AVL树,那我就可以在logn的时间内查询x的范围,如果我在这颗二叉树的每一个节点上都有序的存上以这个节点为根的子树的所有y值,那我就可以在logn的时间确定x之后再以logn的时间用二分确定y的范围,这样就可以做到一次查询的时间复杂度为\(O(logn)\)。剩下一个问题就是要快速的在每个节点有序的存上y值,真巧,这是颗二叉树,根节点的y值数组就等于左子树+右子树的y值数组,可以在\(O(n)\)的时间把左右子树两个有序数组合并为一个有序数组。这样建树和查询的时间复杂度都非常理想。
- 遇到的问题:
- kd树理论上的复杂度是可以解决这个问题的,可能是我写的太丑被卡常了,有4个点一直tle
- 在我的代码中,建每个节点的y数组时,调用过qsort()这个函数,找了好久bug才发现,只要调用这个函数就会出问题,至今原因不明
- 虽然是按x坐标建了颗AVL树,但是我在最终查询的时候好像只是把它当做普通的二叉树在用……
- 时间和空间复杂度:时间复杂度:\(O(mlogn)\),空间复杂度\(O(nlogn)\)
- 特别或创新:整个方法都是一种创新,由R树的时空复杂度想到了这种解题办法
代码一(80分的kd树):
#include "cstdio"
#include "temperature.h"
#include "stdlib.h"
#define inf 1000000000
#define mod 1000000007
#define ll long long
#define eps 1e-12
using namespace std;
int n, D;
ll lastans;
struct P {
int d[2], mx[2], mn[2], v, l, r;
ll sum, cnt;
int &operator[](int x) {
return d[x];
}
friend bool operator==(P a, P b) {
return a.d[0] == b.d[0] && a.d[1] == b.d[1];
}
friend bool operator<(P a, P b) {
return a[D] < b[D];
}
} p[200005];
int cmp(const void *aa, const void *bb) {
return (*(P *) bb)[D] - (*(P *) aa)[D];
}
bool in(int x1, int y1, int x2, int y2, int X1, int Y1, int X2, int Y2) {
return x1 <= X1 && X2 <= x2 && y1 <= Y1 && Y2 <= y2;
}
bool out(int x1, int y1, int x2, int y2, int X1, int Y1, int X2, int Y2) {
return x1 > X2 || x2 < X1 || y1 > Y2 || y2 < Y1;
}
inline int Max(int a, int b) {
return (a > b ? a : b);
}
inline int Min(int a, int b) {
return (a < b ? a : b);
}
struct data {
P t[200005], now;
int rt, cnt;
ll count, ans;
void update(int k) {
int l = t[k].l, r = t[k].r;
for (int i = 0; i < 2; i++) {
t[k].mn[i] = t[k].mx[i] = t[k][i];
if (l)t[k].mn[i] = Min(t[k].mn[i], t[l].mn[i]);
if (l)t[k].mx[i] = Max(t[k].mx[i], t[l].mx[i]);
if (r)t[k].mn[i] = Min(t[k].mn[i], t[r].mn[i]);
if (r)t[k].mx[i] = Max(t[k].mx[i], t[r].mx[i]);
}
t[k].sum = t[k].v + t[l].sum + t[r].sum;
t[k].cnt = 1 + t[l].cnt + t[r].cnt;
}
void insert(int &k, bool D) {
if (!k) {
k = ++cnt;
t[k][0] = t[k].mn[0] = t[k].mx[0] = now[0];
t[k][1] = t[k].mn[1] = t[k].mx[1] = now[1];
}
if (now == t[k]) {
t[k].v += now.v, t[k].sum += now.v;
return;
}
if (now[D] < t[k][D])insert(t[k].l, D ^ 1);
else insert(t[k].r, D ^ 1);
update(k);
}
void query(int k, int x1, int y1, int x2, int y2) {
if (!k)return;
ll tmp = 0;
if (in(x1, y1, x2, y2, t[k].mn[0], t[k].mn[1], t[k].mx[0], t[k].mx[1])) {
ans += t[k].sum;
count += t[k].cnt;
return;
}
if (out(x1, y1, x2, y2, t[k].mn[0], t[k].mn[1], t[k].mx[0], t[k].mx[1]))return;
if (in(x1, y1, x2, y2, t[k][0], t[k][1], t[k][0], t[k][1])) {
ans += t[k].v;
count++;
}
query(t[k].l, x1, y1, x2, y2);
query(t[k].r, x1, y1, x2, y2);
}
int rebuild(int l, int r, bool f) {
if (l > r)return 0;
int mid = (l + r) >> 1;
D = f;
qsort(p + l, r - l + 1, sizeof(p[0]), cmp);
t[mid] = p[mid];
t[mid].l = rebuild(l, mid - 1, f ^ 1);
t[mid].r = rebuild(mid + 1, r, f ^ 1);
update(mid);
return mid;
}
} T;
int main() {
n = GetNumOfStation();
if (n == 0) {
int x1, x2, y1, y2;
while (GetQuery(&x1, &y1, &x2, &y2)) {
Response(0);
printf("0\n");
}
return 0;
}
int opt, x, y, x2, y2, A, m = 10000;
for (int i = 0; i < n; i++) {
GetStationInfo(i, &p[i + 1][0], &p[i + 1][1], &p[i + 1].v);
p[i + 1].sum = p[i + 1].v;
}
T.rt = T.rebuild(1, n, 0);
while (GetQuery(&x, &y, &x2, &y2)) {
T.count = 0;
T.ans = 0;
T.query(T.rt, x, y, x2, y2);
if (T.count != 0) {
Response(T.ans / T.count);
} else {
Response(0);
}
}
return 0;
}
代码二(AC100分的代码):
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include "temperature.h"
using namespace std;
const long long inf = 0x3f3f3f3f3f3f;
typedef long long ll;
const int maxn = 1e5;
int cmp(const void *a, const void *b);
struct yav;
template<typename TtT>
class Vector {
private:
TtT *Data;
int Len, Size;
public:
inline Vector() {
Data = NULL;
Len = Size = 0;
}
inline Vector(const Vector &other) {
if (this == &other || !Len)return;
Data = (TtT *) malloc(sizeof(TtT) * other.Len);
for (int i = 0; i < other.Size; i++)Data[i] = other.Data[i];
Len = other.Len, Size = other.Size;
}
inline TtT &operator[](const int x) {
return Data[x];
}
const Vector &push_back(const TtT x) {
if (Size >= Len) {
Len = (Len == 0 ? 1 : Len << 1);
TtT *newData = (TtT *) malloc(sizeof(TtT) * Len);
memcpy(newData, Data, Size * sizeof(TtT));
free(Data);
Data = newData;
}
Data[Size++] = x;
return *this;
}
const Vector &pop_back() {
Size--;
if (Size == (Len >> 2)) {
Len = Len >> 1;
TtT *newData = (TtT *) malloc(sizeof(TtT) * Len);
memcpy(newData, Data, Size * sizeof(TtT));
free(Data);
Data = newData;
}
return *this;
}
inline unsigned int size() {
return Size;
}
inline unsigned int len() {
return Len;
}
inline void sort() {//用这个sort就会出错不知道为什么
qsort(Data, Size, sizeof(TtT), cmp);
}
};
ll answer, count;
struct yav {//y and val
int y, val;
} bbb[maxn];
int cmp(const void *a, const void *b) {
return (*(yav *) a).y - (*(yav *) b).y;
}
typedef struct Node {
int BF;
ll x, y, temperature;
ll maxx, minx;
Node() {
maxx = -inf;
minx = inf;
}
Vector<long long> ysorted, temp, d;
Vector<yav> xtemp;
struct Node *lc, *rc;
} Node, *Tree;
void LR(Tree *p) { //左旋
Tree R;
R = (*p)->rc;
(*p)->rc = R->lc;
R->lc = (*p);
*p = R;
}
void RR(Tree *p) { //右旋
Tree L;
L = (*p)->lc;
(*p)->lc = L->rc;
L->rc = (*p);
*p = L;
}
void LB(Tree *T) {
Tree L, Lr;
L = (*T)->lc;
switch (L->BF) {
case 1://新节点插入在T的左孩子的左子树上,做单右旋处理
(*T)->BF = L->BF = 0;
RR(T);
break;
case -1://新插入节点在T的左孩子的右子树上,做双旋处理
Lr = L->rc;
switch (Lr->BF) {
case 1:
(*T)->BF = -1;
L->BF = 0;
break;
case 0:
(*T)->BF = L->BF = 0;
break;
case -1:
(*T)->BF = 0;
L->BF = 1;
break;
}
Lr->BF = 0;
LR(&(*T)->lc);
RR(T);
}
}
void RB(Tree *T) {
Tree R, Rl;
R = (*T)->rc;
switch (R->BF) {
case -1://新节点插在T的右孩子的右子树上,要做单左旋处理
(*T)->BF = R->BF = 0;
LR(T);
break;
case 1://新节点插在T的右孩子的左子树上,要做双旋处理
Rl = R->lc;
switch (Rl->BF) {
case 1:
(*T)->BF = 0;
R->BF = -1;
break;
case 0:
(*T)->BF = R->BF = 0;
break;
case -1:
(*T)->BF = 1;
R->BF = 0;
break;
}
Rl->BF = 0;
RR(&(*T)->rc);
LR(T);
}
}
bool insert(Tree *T, int x, int y, int val, bool *taller) { //变量taller反应T长高与否
if (!*T) {
*T = new Node;
(*T)->x = x;
(*T)->y = y;
(*T)->temperature = val;
(*T)->lc = (*T)->rc = NULL;
(*T)->BF = 0;
(*T)->xtemp.push_back({y, val});
*taller = true;
} else {
if (x == (*T)->x) { //不插入,将y和val加入
*taller = false;
(*T)->xtemp.push_back({y, val});
return false;
}
if (x < (*T)->x) {
//以下为左子树插入
if (!insert(&(*T)->lc, x, y, val, taller))//未插入
return false;
if (*taller) { //插入左子树,左子树深度增加
switch ((*T)->BF) {
case 1://深度若为1,则开始调整
LB(T);
*taller = false;
break;
case 0://左右子树等深,左子树变深
(*T)->BF = 1;
*taller = true;
break;
case -1://右子树比左子树深,左右子树等深
(*T)->BF = 0;
*taller = false;
break;
}
}
} else {
//以下为右子树插入
if (!insert(&(*T)->rc, x, y, val, taller))
return false;
if (*taller) { //插入右子树,右子树深度增加
switch ((*T)->BF) {
case 1://左子树比右子树深,左右子树等深
(*T)->BF = 0;
*taller = false;
break;
case 0://左右子树等深,右子树变深
(*T)->BF = -1;
*taller = true;
break;
case -1://深度若为-1,则开始调整
RB(T);
*taller = false;
break;
}
}
}
}
return true;
}
inline ll Max(ll a, ll b) {
if (a > b) return a;
else return b;
}
inline ll Min(ll a, ll b) {
if (a < b) return a;
else return b;
}
void merge1(Tree *T, int l, int mid, int r) {
if (l == r) return;
merge1(&(*T), l, (l + mid) >> 1, mid);
merge1(&(*T), mid + 1, (mid + 1 + r) >> 1, r);
int i = l, j = mid + 1;
for (int k = l; k <= r; k++) {
if (j > r || (i <= mid && (*T)->xtemp[i].y < (*T)->xtemp[j].y)) bbb[k] = (*T)->xtemp[i++];
else bbb[k] = (*T)->xtemp[j++];
}
for (int k = l; k <= r; k++)
(*T)->xtemp[k] = bbb[k];
}
void build(Tree *T) {
if ((*T) == NULL) return;
build(&((*T)->lc));
build(&((*T)->rc));
(*T)->maxx = (*T)->x;
(*T)->minx = (*T)->x;
if ((*T)->lc != NULL) {
(*T)->maxx = Max((*T)->maxx, (*T)->lc->maxx);
(*T)->minx = Min((*T)->minx, (*T)->lc->minx);
}
if ((*T)->rc != NULL) {
(*T)->maxx = Max((*T)->maxx, (*T)->rc->maxx);
(*T)->minx = Min((*T)->minx, (*T)->rc->minx);
}
//调用这个sort排序就会出问题,所以后来手写了个归并
// (*T)->xtemp.sort();
int too = (*T)->xtemp.size() - 1;
merge1(&(*T), 0, too / 2, too);
Vector<long long> ytemp, valtemp;
int lsize = 0, rsize = 0, tsize = 0;
if ((*T)->lc != NULL)
lsize = (*T)->lc->ysorted.size() - 1;
if ((*T)->rc != NULL)
rsize = (*T)->rc->ysorted.size() - 1;
tsize = (*T)->xtemp.size();
int nowt, nowl, nowr;
nowt = 0, nowl = 1;
while (nowt < tsize && nowl < lsize) {
if ((*T)->lc->ysorted[nowl] < (*T)->xtemp[nowt].y) {
ytemp.push_back((*T)->lc->ysorted[nowl]);
valtemp.push_back((*T)->lc->temp[nowl++]);
} else {
ytemp.push_back((*T)->xtemp[nowt].y);
valtemp.push_back((*T)->xtemp[nowt++].val);
}
}
while (nowt < tsize) {
ytemp.push_back((*T)->xtemp[nowt].y);
valtemp.push_back((*T)->xtemp[nowt++].val);
}
while (nowl < lsize) {
ytemp.push_back((*T)->lc->ysorted[nowl]);
valtemp.push_back((*T)->lc->temp[nowl++]);
}
int to = ytemp.size();
nowr = 1, nowt = 0;
(*T)->ysorted.push_back(-inf);
(*T)->temp.push_back(0);
while (nowr < rsize && nowt < to) {
if ((*T)->rc->ysorted[nowr] < ytemp[nowt]) {
(*T)->ysorted.push_back((*T)->rc->ysorted[nowr]);
(*T)->temp.push_back((*T)->rc->temp[nowr++]);
} else {
(*T)->ysorted.push_back(ytemp[nowt]);
(*T)->temp.push_back(valtemp[nowt++]);
}
}
while (nowr < rsize) {
(*T)->ysorted.push_back((*T)->rc->ysorted[nowr]);
(*T)->temp.push_back((*T)->rc->temp[nowr++]);
}
while (nowt < to) {
(*T)->ysorted.push_back(ytemp[nowt]);
(*T)->temp.push_back(valtemp[nowt++]);
}
to = (*T)->ysorted.size();
(*T)->ysorted.push_back(inf);
(*T)->temp.push_back(0);
(*T)->d.push_back(0);
for (int i = 1; i < to; i++) {
(*T)->d.push_back((*T)->d[i - 1] + (*T)->temp[i]);
}
}
void slove(Tree *T, int y1, int y2) {
int l = 0, r = (*T)->ysorted.size() - 1;
if ((*T)->ysorted[1] > y2 || (*T)->ysorted[r - 1] < y1) return;
int mid;
int from, to;
while (l < r) {
mid = (l + r + 1) >> 1;
if ((*T)->ysorted[mid] >= y1) r = mid - 1;
else l = mid;
}
from = l;
l = 0, r = (*T)->ysorted.size() - 1;
while (l < r) {
mid = (l + r) >> 1;
if ((*T)->ysorted[mid] <= y2) l = mid + 1;
else r = mid;
}
to = l;
count = count + to - from - 1;
answer = answer + (*T)->d[to - 1] - (*T)->d[from];
}
void slove2(Tree *T, int y1, int y2) {
for (int i = 0; i < (*T)->xtemp.size(); i++) {
if ((*T)->xtemp[i].y >= y1 && (*T)->xtemp[i].y <= y2) {
answer += (*T)->xtemp[i].val;
count++;
}
}
}
void query(Tree *T, int x1, int y1, int x2, int y2) {
if ((*T) == NULL) return;
if ((*T)->minx >= x1 && (*T)->maxx <= x2) {
slove(&(*T), y1, y2);
return;
}
if ((*T)->maxx < x1 || (*T)->minx > x2) return;
if ((*T)->x >= x1 && (*T)->x <= x2) {
slove2(&(*T), y1, y2);
}
query(&(*T)->lc, x1, y1, x2, y2);
query(&(*T)->rc, x1, y1, x2, y2);
}
int main() {
//freopen("in.txt", "r", stdin);
int n, q;
n = GetNumOfStation();
int x, y, val;
Tree T = NULL;
bool taller;
for (int i = 0; i < n; i++) {
GetStationInfo(i, &x, &y, &val);
insert(&T, x, y, val, &taller);
}
build(&T);
int x1, y1, x2, y2;
while (GetQuery(&x1, &y1, &x2, &y2)) {
answer = 0, count = 0;
query(&T, x1, y1, x2, y2);
if (count == 0) {
Response(0);
} else {
Response(answer / count);
}
}
return 0;
}