2018-2019 ACM-ICPC, Asia Shenyang Regional Contest E. The Kouga Ninja Scrolls 不容易的线段树 切比雪夫距离

本文解析了一道复杂的数据结构与算法题目,通过使用线段树维护二维坐标上的人员位置和阵营信息,实现对曼哈顿距离的有效查询。详细介绍了如何将曼哈顿距离转化为切比雪夫距离,以及线段树如何维护最大值、最小值、次大值和次小值,确保不同阵营间最远距离的准确计算。
摘要由CSDN通过智能技术生成

题目链接:http://codeforces.com/gym/101955/problem/E

 

题意:

        二维坐标上给你1e5个人,并且给你这些人分别所属的阵营,现在你有三种操作。

        1 k x y: 表示你要将第k个人的坐标增加(x,y),即变成(x0+x,y0+y),(原来在x0,y0上)。

        2 k c: 表示将第k个人的阵营进行改变,变为c

        3 l  r :表示询问第l个人到第r个人之间,处于不同阵营的最远曼哈顿距离。

做法:

        首先,我们需要知道一个叫切比雪夫距离的东西,简单说就是两个点(x1,y1),(x2,y2)之间的距离=max(abs(x1-x2),abs(y1-y2)),并且我们要知道这个东西是可以和曼哈顿距离相互转化的东西。即原坐标中的(x,y)在新坐标系中变为(x+y,x-y),就可以直接用切比雪夫距离来表示曼哈顿距离,至于为什么呢,因为曼哈顿距离=abs(x1-x2)+abs(y1-y2),假设这两个绝对值里面出来的都是同号(即x1>x2,y1>y2或者x1<x2,y1<x2),那么就是等于x1+y1-(x2+y2),或者x2+y2-(x1+y1),那么就是新坐标中的两个新的Xnew值相减,两个绝对值中为异号则是看y,所以我们在新坐标中取x差和y的差的最大值。

       如果还是不是很懂的话就戳 https://www.cnblogs.com/zwfymqz/p/8253530.html?tdsourcetag=s_pctim_aiomsg  ,感觉讲的应该很彻底了,还有把切比雪夫距离转换成曼哈顿距离的。

       在知道了这样的规律之后,那么我们就可以用x和y去做曼哈顿距离,就可以用线段树进行区间x值和y值的最大值和最小值的维护,但这里有个问题,就是可能我们的最大值和最小值都是同一个阵营的,这样的话就会找不到答案,所以我们在线段树里要维护一个最大值最小值还有一个次大值和次小值,注意一定要保证次大值和最大值是阵营是不一样的(因为同一个阵营的话距离就没意义了,所以需要不同阵营),每次查询的时候要看最大值和最小值,次大和最小,最大和次小值的最大差(在保证阵营的前提下),次大和次小其实应该没必要去查询。

        总的来说就是线段树要维护8个pair,代码真是极其难敲...bug还de了好久。下面代码里也写了点注释..加油叭= =.


#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define fi first
#define se second
#define rep(i,a,b) for(int i=(int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll inf=1e16;

typedef pair<ll,int> pii;

ll x[maxn],px[maxn],y[maxn],py[maxn],c[maxn];
pii xmax[maxn<<2][2],xmin[maxn<<2][2];
pii ymax[maxn<<2][2],ymin[maxn<<2][2];
//0表示最值 1表示次值
//first是val second是阵营

int n,m;
void fresh(pii aim[][2],pii tmp[],int rt,int f){
    if(f){//最大值的更新
        aim[rt][0]={-inf,0},aim[rt][1]={-inf,0};
        for(int i=0;i<4;i++){
            if(tmp[i].fi>aim[rt][0].fi&&tmp[i].se){
                aim[rt][0]=tmp[i];
            }
        }
        for(int i=0;i<4;i++){
            if(tmp[i].fi>aim[rt][1].fi&&tmp[i].se!=aim[rt][0].se){
                aim[rt][1]=tmp[i];
            }
        }
    }
    else{//最小值的更新
        aim[rt][0]={inf,0},aim[rt][1]={inf,0};
        for(int i=0;i<4;i++){
            if(tmp[i].fi<aim[rt][0].fi&&tmp[i].se){
                aim[rt][0]=tmp[i];
            }
        }
        for(int i=0;i<4;i++){
            if(tmp[i].fi<aim[rt][1].fi&&tmp[i].se!=aim[rt][0].se){
                aim[rt][1]=tmp[i];
            }
        }
    }
}
void push_up(int rt){
    pii tmp[4];
    tmp[0]=xmax[lson][0],tmp[1]=xmax[rson][0],tmp[2]=xmax[lson][1],tmp[3]=xmax[rson][1];
    fresh(xmax,tmp,rt,1);
    tmp[0]=ymax[lson][0],tmp[1]=ymax[rson][0],tmp[2]=ymax[lson][1],tmp[3]=ymax[rson][1];
    fresh(ymax,tmp,rt,1);
    tmp[0]=xmin[lson][0],tmp[1]=xmin[rson][0],tmp[2]=xmin[lson][1],tmp[3]=xmin[rson][1];
    fresh(xmin,tmp,rt,0);
    tmp[0]=ymin[lson][0],tmp[1]=ymin[rson][0],tmp[2]=ymin[lson][1],tmp[3]=ymin[rson][1];
    fresh(ymin,tmp,rt,0);
}
void build(int l,int r,int rt){
    if(l==r){
        xmax[rt][0]={x[l],c[l]}; xmax[rt][1]={-inf,0};
        xmin[rt][0]={x[l],c[l]}; xmin[rt][1]={inf,0};
        ymax[rt][0]={y[l],c[l]}; ymax[rt][1]={-inf,0};
        ymin[rt][0]={y[l],c[l]}; ymin[rt][1]={inf,0};
        return ;
    }
    int mid=(r+l)/2;
    build(l,mid,lson);
    build(mid+1,r,rson);
    push_up(rt);
}
void update(int l,int r,int rt,int pos){
    if(l==r){
        xmax[rt][0]={x[l],c[l]}; xmax[rt][1]={-inf,0};
        xmin[rt][0]={x[l],c[l]}; xmin[rt][1]={inf,0};
        ymax[rt][0]={y[l],c[l]}; ymax[rt][1]={-inf,0};
        ymin[rt][0]={y[l],c[l]}; ymin[rt][1]={inf,0};
        return ;
    }
    int mid=(r+l)/2;
    if(pos<=mid) update(l,mid,lson,pos);
    else if(pos>mid) update(mid+1,r,rson,pos);
    push_up(rt);
}
void deal(pii big,pii sma,ll &change){
    if(big.se!=sma.se&&big.se!=0&&sma.se!=0){
        change=max(change,(ll)(big.fi-sma.fi));
    }
}
void query(int l,int r,int rt,int ql,int qr,pii &big0,pii &big1,pii &sma0,pii &sma1,int flag){
    if(ql<=l&&r<=qr){
        if(flag){//查询x
            if(big0.fi<xmax[rt][0].fi) {
                //保证在新的阵营和旧的阵营不同的时候
                //才能把原来的最大值变成次大值
                if(xmax[rt][0].se!=big0.se) big1=big0;
                big0=xmax[rt][0];
            }
            for(int i=0;i<2;i++){
                if(big1.fi<xmax[rt][i].fi&&xmax[rt][i].se!=big0.se) {
                    big1=xmax[rt][i];
                }
            }
            if(sma0.fi>xmin[rt][0].fi) {
                if(xmin[rt][0].se!=sma0.se) sma1=sma0;
                sma0=xmin[rt][0];
            }
            for(int i=0;i<2;i++){
                if(sma1.fi>xmin[rt][i].fi&&xmin[rt][i].se!=sma0.se) {
                    sma1=xmin[rt][i];
                }
            }
        }
        else{
            if(big0.fi<ymax[rt][0].fi) {
                if(ymax[rt][0].se!=big0.se) big1=big0;
                big0=ymax[rt][0];
            }
            for(int i=0;i<2;i++){
                if(big1.fi<ymax[rt][i].fi&&ymax[rt][i].se!=big0.se) {
                    big1=ymax[rt][i];
                }
            }
            if(sma0.fi>ymin[rt][0].fi) {
                if(ymin[rt][0].se!=sma0.se) sma1=sma0;
                sma0=ymin[rt][0];
            }
            for(int i=0;i<2;i++){
                if(sma1.fi>ymin[rt][i].fi&&ymin[rt][i].se!=sma0.se) {
                    sma1=ymin[rt][i];
                }
            }
        }
        return ;
    }
    int mid=(r+l)/2;
    if(ql<=mid) query(l,mid,lson,ql,qr,big0,big1,sma0,sma1,flag);
    if(qr>mid) query(mid+1,r,rson,ql,qr,big0,big1,sma0,sma1,flag);
}
int main(){
    int T,cas=0; scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        rep(i,1,n){
            scanf("%lld%lld%lld",&px[i],&py[i],&c[i]);
            x[i]=px[i]+py[i],y[i]=px[i]-py[i];
        }
        build(1,n,1);
        printf("Case #%d:\n",++cas);
        rep(i,1,m){
            int op,ind,l,r,cc;
            ll xx,yy;
            scanf("%d",&op);
            if(op==1){
                scanf("%d%lld%lld",&ind,&xx,&yy);
                px[ind]+=xx,py[ind]+=yy;
                x[ind]=px[ind]+py[ind],y[ind]=px[ind]-py[ind];
                update(1,n,1,ind);
            }
            if(op==2){
                scanf("%d%d",&ind,&cc);
                c[ind]=cc;
                update(1,n,1,ind);
            }
            if(op==3){
                scanf("%d%d",&l,&r);
                ll ansx=0,ansy=0;
                pii big0={-inf,0},big1={-inf,0},sma1={inf,0},sma0={inf,0};
                query(1,n,1,l,r,big0,big1,sma0,sma1,1);
                deal(big0,sma0,ansx);
                deal(big1,sma0,ansx);
                deal(big0,sma1,ansx);
                deal(big1,sma1,ansx);
                big0={-inf,0},big1={-inf,0},sma1={inf,0},sma0={inf,0};
                query(1,n,1,l,r,big0,big1,sma0,sma1,0);
                deal(big0,sma0,ansy);
                deal(big1,sma0,ansy);
                deal(big0,sma1,ansy);
                deal(big1,sma1,ansy);
                printf("%lld\n",max(ansx,ansy));
            }
        }
    }
    return  0;
}
/*
10
4 10
1 1 3
5 1 1
2 5 2
4 4 1

3 2 4
1 4 3 4
3 2 4

3 1 3
1 1 1 3
3 1 3

3 3 4
2 3 1
3 3 4

*/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值