平均氣溫(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;
}