[BZOJ4170]极光
Description
天空中出现了许多的北极光,这些北极光组成了一个长度为n的正整数数列a[i],远古之魔书上记载到:
2个位置的graze值为两者位置差与数值差的和:
graze(x,y)=|x-y|+|a[x]-a[y]|。
要想破解天罚,就必须支持2种操作(k都是正整数):
Modify x k:将第x个数的值修改为k。
Query x k:询问有几个i满足graze(x,i)<=k。
由于从前的天罚被圣王lmc破解了,所以rhl改进了她的法术,询问不仅要考虑当前数列,还要考虑任意历史版本,
即统计任意位置上出现过的任意数值与当前的a[x]的graze值<=k的对数。(某位置多次修改为同样的数值,按多次
统计)
Input
第1行两个整数n,q。分别表示数列长度和操作数。
第2行n个正整数,代表初始数列。
第3~q+2行每行一个操作。
N<=40000, 修改操作数<=60000, 询问操作数<=10000, Max{a[i]}(含修改)<=80000
Output
对于每次询问操作,输出一个非负整数表示答案
Sample Input
3 5
2 4 3
Query 2 2
Modify 1 3
Query 2 2
Modify 1 2
Query 1 1
Sample Output
2
3
3
题解
曼哈顿距离转切比雪夫距离
首先可以发现把\((i,val_i)\)映射到坐标系上的话题目要求的就是找到与这个点的曼哈顿距离\(\le k\)的点的个数
但是曼哈顿距离不好处理
我们可以将其转化成切比雪夫距离来处理
简单介绍一下:
曼哈顿距离:对于点\((x_1,y_1),(x_2,y_2)\),曼哈顿距离\(=|x1-x2|+|y1-y2|\)
切比雪夫距离:对于点\((x_1,y_1),(x_2,y_2)\),切比雪夫距离\(=max(|x1-x2|,|y1-y2|)\)
然后曼哈顿距离和切比雪夫距离是可以互相转化的:
曼哈顿距离转切比雪夫距离:\((x,y) \to (x+y , x-y)\)
切比雪夫距离转曼哈顿距离 : \((x , y) \to (\frac{x+y}{2} , \frac{x-y}{2})\)
总体的感觉就是切比雪夫距离就是把曼哈顿距离转一下再放大一下
那么对于这道题
就把\((i,val_i)\)转成\((i+val_i,i-val_i)\)
然后查询的时候就直接查切比雪夫距离与点\((i+val_i,i-val_i)\le k\)的点的个数即可
可以用\(KDTree\)解决
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 100005 ;
using namespace std ;
inline int read() {
char c = getchar() ; int x = 0 , w = 1 ;
while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
return x*w ;
}
int n , Num , val[M] ;
int tot , rtp , K ;
struct Poi {
int x[2] ;
} p[M] ;
struct Node {
Poi tp ;
int lsn , rsn , sz , tmi[2] , tmx[2] ;
} t[M] ;
inline bool operator < (Poi a , Poi b) {
return a.x[K] < b.x[K] ;
}
# define ls t[now].lsn
# define rs t[now].rsn
inline void pushup(int now) {
for(int k = 0 ; k <= 1 ; k ++) {
t[now].tmi[k] = t[now].tmx[k] = t[now].tp.x[k] ;
if(ls) {
t[now].tmi[k] = min(t[now].tmi[k] , t[ls].tmi[k]) ;
t[now].tmx[k] = max(t[now].tmx[k] , t[ls].tmx[k]) ;
}
if(rs) {
t[now].tmi[k] = min(t[now].tmi[k] , t[rs].tmi[k]) ;
t[now].tmx[k] = max(t[now].tmx[k] , t[rs].tmx[k]) ;
}
}
t[now].sz = t[ls].sz + t[rs].sz + 1 ;
}
int build(int l , int r , int W) {
if(l > r) return 0 ; K = W ;
int cnt = ++ tot , mid = (l + r) >> 1 ;
nth_element(p + l , p + mid , p + r + 1) ;
t[cnt].tp = p[mid] ;
t[cnt].lsn = build(l , mid - 1 , (W ^ 1)) ;
t[cnt].rsn = build(mid + 1 , r , (W ^ 1)) ;
pushup(cnt) ; return cnt ;
}
void Insert(Poi po , int &now , int W) {
if(!now) {
now = ++ tot ; t[now].tp = po ;
pushup(now) ; return ;
}
if(po.x[W] <= t[now].tp.x[W]) Insert(po , ls , (W ^ 1)) ;
else Insert(po , rs , (W ^ 1)) ;
pushup(now) ;
}
inline bool Allin(int ax , int ay , int bx , int by , int Ax , int Ay , int Bx , int By) {
return (Ax >= ax && Ay >= ay && Bx <= bx && By <= by) ;
}
inline bool Allout(int ax , int ay , int bx , int by , int Ax , int Ay , int Bx , int By) {
return (Ax > bx || Ay > by || Bx < ax || By < ay) ;
}
int query(int lx , int ly , int rx , int ry , int now) {
if(!now) return 0 ; int ret = 0 ;
if(Allin(lx , ly , rx , ry , t[now].tmi[0] , t[now].tmi[1] , t[now].tmx[0] , t[now].tmx[1])) return t[now].sz ;
if(Allout(lx , ly , rx , ry , t[now].tmi[0] , t[now].tmi[1] , t[now].tmx[0] , t[now].tmx[1])) return 0 ;
if(Allin(lx , ly , rx , ry , t[now].tp.x[0] , t[now].tp.x[1] , t[now].tp.x[0] , t[now].tp.x[1]) ) ++ ret ;
ret += query(lx , ly , rx , ry , ls) + query(lx , ly , rx , ry , rs) ;
return ret ;
}
# undef ls
# undef rs
int main() {
n = read() ; int Case = read() ;
for(int i = 1 ; i <= n ; i ++) val[i] = read() ;
for(int i = 1 ; i <= n ; i ++) {
p[i].x[0] = i + val[i] ,
p[i].x[1] = i - val[i] ;
}
Num = n ;
rtp = build(1 , Num , 0) ;
char opt[10] ; int x , y , Tx , Ty ;
while(Case --) {
scanf("%s",opt) ;
x = read() ; y = read() ;
if(opt[0] == 'M') {
val[x] = y ;
p[++Num].x[0] = x + y ;
p[Num].x[1] = x - y ;
Insert(p[Num] , rtp , 0) ;
if(Num % 10001 == 0) {
tot = 0 ;
rtp = build(1 , Num , 0) ;
}
}
else {
Tx = x + val[x] , Ty = x - val[x] ;
printf("%d\n",query(Tx - y , Ty - y , Tx + y , Ty + y , rtp)) ;
}
}
return 0 ;
}