线段树区间合并+k维空间的曼哈顿距离——cf1093G好题

和去年多校的CSGO一样,用状态压缩来求Manhattan距离的最大值

然后要用线段树维护一下区间最大值

/*
k维空间给定n个点,两个操作
1 i b1 b2 .. bk : 修改第i个点的坐标
2 l r:询问[l,r]区间点最大的曼哈顿距离 

先考虑不带修:
    线段树维护区间2^5种状态的最大值 
    查询时只要求出相对的两个状态的最大值即可
    
关于这个贪心的证明:
    首先因为绝对值,所以aij前面带的符号可能是-也可能是+,总共就是有关2^k种可能
    那么考虑每种状态 S 的最大值,加上相对这种状态 (1<<k)-S 的最大值,来维护答案 
    其实会有很多情况是不应该存在的,比如aij是第j维最小的,那么其前面的符号就只能是-
    可是当其前面的符号是+时也被考虑进去了,其实能够保证另有一个数比这种情况大
    即这种情况对结果不造成影响        
    
再来考虑待修的情况
    修改pos位置的点,就要去线段树中更新和这个点有关的2^5种状态

所以还是线段树维护区间最大值的问题,可以用一个结点来维护 
*/
#include<bits/stdc++.h>
using namespace std;
#define maxn 200005
int k,n,m,a[maxn][10],v[maxn][1<<5]; 

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct Node{int Max,val[1<<5];}p[maxn<<2];
void pushup(int rt){
    for(int i=0;i<(1<<5);i++)
        p[rt].val[i]=max(p[rt<<1].val[i],p[rt<<1|1].val[i]);
    p[rt].Max=-0x3f3f3f3f;
    for(int i=0;i<(1<<5);i++)
        p[rt].Max=max(p[rt].Max,p[rt].val[i]+p[rt].val[(1<<5)-1-i]);
}
void build(int l,int r,int rt){
    if(l==r){
        for(int i=0;i<(1<<5);i++){
            p[rt].val[i]=v[l][i];
//            p[rt].Max=max(p[rt].Max,v[l][i]);
        }
        return;
    }
    int m=l+r>>1;
    build(lson);build(rson);
    pushup(rt); 
}
void update(int pos,int l,int r,int rt){//更新第pos个点 
    if(l==r){
        for(int i=0;i<(1<<5);i++)
            p[rt].val[i]=v[l][i];
        return;
    }
    int m=l+r>>1;
    if(pos<=m)update(pos,lson);
    else update(pos,rson);
    pushup(rt);
}
Node merge(Node a,Node b){
    for(int i=0;i<(1<<5);i++)
        a.val[i]=max(a.val[i],b.val[i]);
    for(int i=0;i<(1<<5);i++)
        a.Max=max(a.Max,a.val[i]+b.val[(1<<5)-i-1]);
    return a;
}
Node query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r)
        return p[rt];
    int m=l+r>>1;
    Node res;
    for(int i=0;i<(1<<5);i++)
        res.val[i]=-0x3f3f3f3f;
    res.Max=-0x3f3f3f3f;
        
    if(L<=m)
        res=merge(res,query(L,R,lson));
    if(R>m)    
        res=merge(res,query(L,R,rson));
    return res;
}

int main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        for(int j=0;j<k;j++)
            scanf("%d",&a[i][j]);
            
        for(int S=0;S<(1<<5);S++){
            for(int j=0;j<5;j++)
                if(S&(1<<j))v[i][S]+=a[i][j];
                else v[i][S]-=a[i][j];
        }
    }
    
    build(1,n,1);
    
    cin>>m;
    int op,l,r,pos;
    while(m--){
        scanf("%d",&op);
        if(op==1){
            scanf("%d",&pos);
            for(int i=0;i<k;i++)
                scanf("%d",&a[pos][i]);
            for(int S=0;S<(1<<5);S++){
                v[pos][S]=0;
                for(int j=0;j<5;j++)
                    if(S&(1<<j))
                        v[pos][S]+=a[pos][j];
                    else v[pos][S]-=a[pos][j];
            }
            update(pos,1,n,1);
        }
        else {
            scanf("%d%d",&l,&r);
            Node node=query(l,r,1,n,1);
            cout<<node.Max<<'\n';
        }
    }
}
 

 

posted on 2019-05-23 17:04  zsben  阅读(335)  评论(0编辑  收藏  举报

导航