【线段树】[BZOJ2104/WC2009]最短路问题

题目描述

这里写图片描述

分析

在一个长条状的东西上维护信息,我们可以想到使用线段树。
对于一个对应范围为 [L,R] 的节点,我们维护区间内最左边的那一列的点的每一个点和最右边一列的每一个点两两之间只经过 [L,R] 的点的最短路。
关于合并,可以查看http://blog.csdn.net/iamzky/article/details/42119193
关于最后求答案呢。
我们求出1..l + l..r +r..n答案,记作 X Y Z
最终答案的构成可能有4种可能。
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
同样是因为只有6行。

然后就A了。

代码

#include<cstdio>
#include<algorithm>
#define INF 0x7fffffffffffffffll
#define MAXN 100000
using namespace std;
template<class T>
void Read(T &x){
    char c;
    bool f(0);
    while(c=getchar(),c!=EOF)
        if(c=='-')
            f=1;
        else if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            if(f)
                x=-x;
            return;
        }
}
typedef long long LL;
int n,m,a[6][MAXN+10];
void read(){
    Read(n);
    int i,j;
    for(i=0;i<6;i++)
        for(j=1;j<=n;j++)
            Read(a[i][j]);
}
struct node{
    LL ll[6][6],lr[6][6],rr[6][6];
    bool empty;
    inline node(){
    }
    inline node(bool empty):empty(empty){
    }
    inline bool operator == (const node &b)const{
        int i,j;
        for(i=0;i<6;i++)
            for(j=0;j<6;j++)
                if(ll[i][j]!=b.ll[i][j]||lr[i][j]!=b.lr[i][j]||rr[i][j]!=b.rr[i][j])
                    return 0;
        return 1;
    }
}tree[(1<<18)+10];
inline node merge(const node &a,const node &b){
    if(a.empty)
        return b;
    if(b.empty)
        return a;
    static LL lm[6][6],rm[6][6];
    node ret(0);
    int i,j,k;
    for(i=0;i<6;i++)
        for(j=0;j<6;j++){
            lm[i][j]=rm[i][j]=ret.lr[i][j]=INF;
            ret.ll[i][j]=a.ll[i][j],ret.rr[i][j]=b.rr[i][j];
        }
    for(k=0;k<6;k++)
        for(i=0;i<6;i++)
            for(j=0;j<6;j++){
                lm[i][j]=min(lm[i][j],a.lr[i][k]+b.ll[k][j]);
                rm[i][j]=min(rm[i][j],a.rr[i][k]+b.lr[k][j]);
            }
    for(k=0;k<6;k++)
        for(i=0;i<6;i++)
            for(j=0;j<6;j++){
                ret.ll[i][j]=min(ret.ll[i][j],lm[i][k]+a.lr[j][k]);
                ret.rr[i][j]=min(ret.rr[i][j],rm[k][i]+b.lr[k][j]);
                ret.lr[i][j]=min(ret.lr[i][j],min(a.lr[i][k]+b.lr[k][j],lm[i][k]+rm[k][j]));
            }
    return ret;
}
void cal(node &b,int l){
    static LL sum[6];
    int i,j;
    sum[0]=a[0][l];
    for(j=1;j<6;j++)
        sum[j]=sum[j-1]+a[j][l];
    for(i=0;i<6;i++)
        for(j=0;j<6;j++)
            b.ll[i][j]=b.lr[i][j]=b.rr[i][j]=i>j?sum[i]-(j?sum[j-1]:0):sum[j]-(i?sum[i-1]:0);
}
void build(int i,int l,int r){
    if(l==r){
        cal(tree[i],l);
        return;
    }
    int mid((l+r)>>1);
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    tree[i]=merge(tree[i<<1],tree[(i<<1)|1]);
}
bool insert(int i,int l,int r,int p){
    if(l==r){
        cal(tree[i],l);
        return 1;
    }
    int mid((l+r)>>1);
    bool f;
    if(p<=mid)
        f=insert(i<<1,l,mid,p);
    else
        f=insert((i<<1)|1,mid+1,r,p);
    if(f){
        node t=merge(tree[i<<1],tree[(i<<1)|1]);
        if(tree[i]==t)
            return 0;
        else{
            tree[i]=t;
            return 1;
        }
    }
    return 0;
}
inline node get_sum(int i,int l,int r,int ll,int rr){
    if(ll<=l&&r<=rr)
        return tree[i];
    if(ll>r||rr<l)
        return 1;
    int mid((l+r)>>1);
    return merge(get_sum(i<<1,l,mid,ll,rr),get_sum((i<<1)|1,mid+1,r,ll,rr));
}
inline LL Query(LL x1,LL y1,LL x2,LL y2){
    node x=get_sum(1,1,n,1,y1),y=get_sum(1,1,n,y1,y2),z=get_sum(1,1,n,y2,n);
    int i,j,k;
    static LL lr[6][6],rl[6][6],ans;
    for(i=0;i<6;i++)
        for(j=0;j<6;j++)
            lr[i][j]=rl[i][j]=y.lr[i][j];
    for(k=0;k<6;k++)
        for(i=0;i<6;i++)
            for(j=0;j<6;j++){
                lr[i][j]=min(lr[i][j],y.lr[i][k]+z.ll[k][j]-a[k][y2]);
                rl[i][j]=min(rl[i][j],x.rr[i][k]+y.lr[k][j]-a[k][y1]);
            }
    ans=y.lr[x1][x2];
    for(i=0;i<6;i++)
        for(j=0;j<6;j++){
            ans=min(ans,y.ll[x1][i]+x.rr[i][j]+y.lr[j][x2]-a[i][y1]-a[j][y1]);
            ans=min(ans,y.lr[x1][i]+z.ll[i][j]+y.rr[j][x2]-a[i][y2]-a[j][y2]);
            ans=min(ans,x.rr[x1][i]+y.lr[i][j]+z.ll[j][x2]-a[i][y1]-a[j][y2]);
            ans=min(ans,lr[x1][i]+y.lr[j][i]+rl[j][x2]-a[i][y2]-a[j][y1]);
        }
    return ans;
}
void solve(){
    build(1,1,n);
    int p,x,y,x2,y2;
    Read(m);
    while(m--){
        Read(p);
        if(p==1){
            Read(x),Read(y),Read(a[x-1][y]);
            insert(1,1,n,y);
        }
        else{
            Read(x),Read(y),Read(x2),Read(y2);
            if(y>y2)
                swap(x,x2),swap(y,y2);
            printf("%lld\n",Query(x-1,y,x2-1,y2));
        }
    }
}
int main()
{
    read();
    solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值