1. 程式人生 > >【bzoj2989】數列 KD-tree

【bzoj2989】數列 KD-tree

數據 ++ class microsoft 出現 cpp ros int 所有

題目描述

給定一個長度為n的正整數數列a[i]。 定義2個位置的graze值為兩者位置差與數值差的和,即graze(x,y)=|x-y|+|a[x]-a[y]|。 2種操作(k都是正整數): 1.Modify x k:將第x個數的值修改為k。 2.Query x k:詢問有幾個i滿足graze(x,i)<=k。因為可持久化數據結構的流行,詢問不僅要考慮當前數列,還要 考慮任意歷史版本,即統計任意位置上出現過的任意數值與當前的a[x]的graze值<=k的對數。(某位置多次修改為 同樣的數值,按多次統計)

輸入

第1行兩個整數n,q。分別表示數列長度和操作數。 第2行n個正整數,代表初始數列。 第3--q+2行每行一個操作。

輸出

對於每次詢問操作,輸出一個非負整數表示答案

樣例輸入

3 5
2 4 3
Query 2 2
Modify 1 3
Query 2 2
Modify 1 2
Query 1 1

樣例輸出

2
3
3


題解

KD-tree

這裏的“可持久化”是逗你玩的,實際上操作只有兩種:在平面上加一個點、在平面上查詢到一個點曼哈頓距離不超過k的點的個數。

KD-tree就可以搞,然而這樣做會TLE,因為查詢斜正方形時間復雜度無法保證。

所以考慮把所有的點繞著原點逆時針旋轉45度,查詢的就是一個矩形空間,就可以直接使用KD-tree。

根據數學知識可知點$(x,y)$旋轉後變為點$(\frac{x-y}{\sqrt 2},\frac{x+y}{\sqrt 2})$,可以把所有的$\sqrt 2$約掉,變為$(x-y,x+y)$。

查詢時查的就是與某點切比雪夫距離不超過k(一個正方形範圍)的點的個數。

親測不加重構跑得比加重構還快,所以不用加了。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define N 100010
using namespace std;
int d , root , g[N];
char str[10];
struct data
{
    int p[2] , mx[2] , mn[2] , sum , c[2];
    bool operator<(data a)const {return p[d] == a.p[d] ? p[d ^ 1]< a.p[d ^ 1] : p[d] < a.p[d];}
}a[N];
void pushup(int x)
{
    int l = a[x].c[0] , r = a[x].c[1];
    a[x].mx[0] = max(a[x].p[0] , max(a[l].mx[0] , a[r].mx[0]));
    a[x].mx[1] = max(a[x].p[1] , max(a[l].mx[1] , a[r].mx[1]));
    a[x].mn[0] = min(a[x].p[0] , min(a[l].mn[0] , a[r].mn[0]));
    a[x].mn[1] = min(a[x].p[1] , min(a[l].mn[1] , a[r].mn[1]));
    a[x].sum = a[l].sum + a[r].sum + 1;
}
int build(int l , int r , int now)
{
    int mid = (l + r) >> 1;
    d = now , nth_element(a + l , a + mid , a + r + 1);
    a[mid].c[0] = a[mid].c[1] = 0;
    if(l < mid) a[mid].c[0] = build(l , mid - 1 , now ^ 1);
    if(r > mid) a[mid].c[1] = build(mid + 1 , r , now ^ 1);
    pushup(mid);
    return mid;
}
void insert(int &k , int x)
{
    if(!k) k = x;
    else if(a[x] < a[k]) d ^= 1 , insert(a[k].c[0] , x);
    else d ^= 1 , insert(a[k].c[1] , x);
    pushup(k);
}
int judge(int k , int x1 , int y1 , int x2 , int y2)
{
    if(!k || a[k].mx[0] < x1 || a[k].mx[1] < y1 || a[k].mn[0] > x2 || a[k].mn[1] > y2) return -1;
    if(a[k].mn[0] >= x1 && a[k].mn[1] >= y1 && a[k].mx[0] <= x2 && a[k].mx[1] <= y2) return 1;
    return 0;
}
int query(int k , int x1 , int y1 , int x2 , int y2)
{
    int tmp = judge(k , x1 , y1 , x2 , y2);
    if(tmp == 1) return a[k].sum;
    if(tmp == -1) return 0;
    int ans = (a[k].p[0] >= x1 && a[k].p[1] >= y1 && a[k].p[0] <= x2 && a[k].p[1] <= y2);
    return ans + query(a[k].c[0] , x1 , y1 , x2 , y2) + query(a[k].c[1] , x1 , y1 , x2 , y2);
}
int main()
{
    a[0].mx[0] = a[0].mx[1] = -1 << 30 , a[0].mn[0] = a[0].mn[1] = 1 << 30;
    int n , m , i , x , y;
    scanf("%d%d" , &n , &m);
    for(i = 1 ; i <= n ; i ++ ) scanf("%d" , &g[i]) , a[i].p[0] = i - g[i] , a[i].p[1] = i + g[i];
    root = build(1 , n , 0);
    for(i = 1 ; i <= m ; i ++ )
    {
        scanf("%s%d%d" , str , &x , &y);
        if(str[0] == ‘M‘) g[x] = y , a[++n].p[0] = x - y , a[n].p[1] = x + y , insert(root , n);
        else printf("%d\n" , query(root , x - g[x] - y , x + g[x] - y , x - g[x] + y , x + g[x] + y));
    }
    return 0;
}

【bzoj2989】數列 KD-tree