1. 程式人生 > >【線段樹】[BZOJ2104/WC2009]最短路問題

【線段樹】[BZOJ2104/WC2009]最短路問題

題目描述

這裡寫圖片描述

分析

在一個長條狀的東西上維護資訊,我們可以想到使用線段樹。
對於一個對應範圍為[L,R]的節點,我們維護區間內最左邊的那一列的點的每一個點和最右邊一列的每一個點兩兩之間只經過[L,R]的點的最短路。
關於合併,可以檢視http://blog.csdn.net/iamzky/article/details/42119193
關於最後求答案呢。
我們求出1..l + l..r +r..n答案,記作XYZ
最終答案的構成可能有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(); }