题目大意
有(n)个在(k)维空间里的点,第(i)个点保存在(a_i)这个长度为(k)的向量中,定义两个点之间的曼哈顿距离是(sumlimits_{i=1}^k|a_{x,i}-a_{y,i}|).给定(q)个询问,每个询问形如下面两种:
- (1space ispace b_1space b_2space b_3...b_k)表示把第(i)个点替换成({b_1,b_2,b_3,...b_k}).
- (2space l space r)表示在两个点(a_i)和(a_j)之间的最大距离,其中(l leq i,jleq r).
数据范围:
(1 leq n leq 2 * 10^5)
(1 leq k leq 5)
(10^{-6} leq a_{i,j} leq 10^6)
(1 leq q leq 2 * 10^5)
数据保证修改的点坐标范围也不会越界,且至少有一个询问操作.
思路
以下在讨论的时候,其中一个点的下标记作是({x1,x2,x3...xk})另外一个点是({y1,y2,y3...yk}).
不妨先考虑这个(k)维版本的子问题:如果只有(2)维应该怎么做,关于这个我之前有篇博客也是一个套路的做法(在(2)维下有更好的形式),这里可以搬过来:
(|x_1 - x_2| + |y_1 - y_2|)
=(max(x_1 - x_2,x_2 - x_1) + max(y_1 - y_2,y_2 - y_1))
=(max((x_1 - x_2)+(y_1 - y_2),(x_1 - x_2)+(y_2 - y_1),(x_2 - x_1) +(y_1 - y_2),(x_2 - x_1)+(y_2 - y_1)))
这里记(s[x][0] = -x1 - x2,s[x][1] = -x1 +x2,s[x][2] = x1 - x2,s[x][3] = x1 +x2)对于(y)也是对称的定义.
那么表达式可以换成:
=(maxlimits_{jin[0,3]}(s[x][j] - s[y][j]))
注意到(s[x][j])的下标(j)与(x_j)的正负取值的关系,对应就是当(j)取(1)的时候是正,取(0)的时候是负的.
也就是说可以写成(s[x][j] = sumlimits_{i=1}^kx_i *((j >> i & 1) ? 1 : -1)).
考虑如果有(n)个点,那么最大值应该是(maxlimits_{kin[0,3]}(max{a_{x,k}} - min{a_{y,k}})).这个式子相当于是在枚举(k),并且找到对于当前的(k)来说哪个点(a_i)的值最大,哪个点对应的值最小,最大减最小就是最大值.那么进而可以把这个做法推广到更高维:由于(kleq 5)所以一共有(2^5)位,每一位取值枚举就可以了,对于查询操作,等于说是在限定的某些点之中找出他们的距离最大值,(k)是可以枚举的一共只有(32)位,剩下的就是找(s[x][k])的最大值和最小值,这部分由于(x)的大小有区间限制,套一个RMQ就可以解决了.
这里使用线段树维护,一个非常粗暴的做法是直接对每个(k)开一个线段树,这样做常数太大了,可以把(32)个值全部压入一个节点里,那么线段树上一个节点储存的就是([l,r])这个区间里最大/小的(s[x][k])的值,记作(maxv[k])和最小值(minv[k]).直接维护就可以了.对于修改操作直接暴力重置最后一个叶子节点再不断pushup上去就可以了.整个操作携带(32)的常数,不影响整体复杂度.查询操作直接枚举(k)就可以了.
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)
const int N = 2e5+7,M = 32,INF = 0x3f3f3f3f;
struct Node
{
int l,r;
int maxv[M],minv[M];
}tr[N * 4];
int a[N][M];
void pushup(int u)
{
forn(k,0,31)
{
tr[u].maxv[k] = max(tr[u << 1].maxv[k],tr[u << 1 | 1].maxv[k]);
tr[u].minv[k] = min(tr[u << 1].minv[k],tr[u << 1 | 1].minv[k]);
}
}
void build(int u,int l,int r)
{
if(l == r)
{
tr[u] = {l,r};
forn(j,0,31)
{
int s_x_j = 0;
forn(i,0,31) s_x_j += a[l][i] * ((j >> i & 1) ? 1 : -1);
tr[u].minv[j] = s_x_j;
tr[u].maxv[j] = s_x_j;
}
return ;
}
int mid = l + r >> 1;
tr[u] = {l,r};
forn(i,0,31) tr[u].minv[i] = INF,tr[u].maxv[i] = -INF;
build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
pushup(u);
}
void modify(int u,int x,vector<int>& a)
{
if(tr[u].l == x && tr[u].r == x)
{
forn(j,0,31)
{
int s_x_j = 0;
forn(i,0,31) s_x_j += a[i] * ((j >> i & 1) ? 1 : -1);
tr[u].minv[j] = s_x_j;
tr[u].maxv[j] = s_x_j;
}
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) modify(u << 1,x,a);
if(x > mid) modify(u << 1 | 1,x,a);
pushup(u);
}
int query_max(int u,int l,int r,int k)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].maxv[k];
int mid = tr[u].l + tr[u].r >> 1,res = -INF;
if(l <= mid) res = max(res,query_max(u << 1,l,r,k));
if(r > mid) res = max(res,query_max(u << 1 | 1,l,r,k));
return res;
}
int query_min(int u,int l,int r,int k)
{
if(tr[u].l >= l && tr[u].r <= r) return tr[u].minv[k];
int mid = tr[u].l + tr[u].r >> 1,res = INF;
if(l <= mid) res = min(res,query_min(u << 1,l,r,k));
if(r > mid) res = min(res,query_min(u << 1 | 1,l,r,k));
return res;
}
int main()
{
int n,k;scanf("%d%d",&n,&k);
forn(i,1,n) forn(j,0,k - 1) scanf("%d",&a[i][j]);
build(1,1,n);
int q;scanf("%d",&q);
while(q--)
{
int op;scanf("%d",&op);
if(op == 1)
{
int x;scanf("%d",&x);
vector<int> a(32,0);
forn(i,0,k - 1) scanf("%d",&a[i]);
modify(1,x,a);
}
else
{
int l,r;scanf("%d%d",&l,&r);
int res = -INF;
forn(k,0,31) res = max(res,query_max(1,l,r,k) - query_min(1,l,r,k));
printf("%d
",res);
}
}
return 0;
}