• CF1093G 高维曼哈顿距离/线段树


    原题链接:Multidimensional Queries

    题目大意

    (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;
    }
    
  • 相关阅读:
    Linux文件系统的设计
    HTML中Select的使用具体解释
    【大话设计模式】—— 工厂方法模式
    C++ Primer 学习笔记_84_模板与泛型编程 --模板特化
    Arcgis API for Android之GPS定位
    “大型票务系统”中对机器恶意訪问的处理——验证码
    hdu 4611
    Java实现 蓝桥杯VIP 算法训练 ALGO-85进制转换
    Java实现 蓝桥杯VIP 算法训练 摆动序列
    Java实现 蓝桥杯VIP 算法训练 摆动序列
  • 原文地址:https://www.cnblogs.com/HotPants/p/14319148.html
Copyright © 2020-2023  润新知