【BZOJ】【P2104】【Wc2009】【shortest】【题解】【线段树】

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2104

一开始想了分块做法,然后发现答案可能绕一圈再回来就放弃了

线段树维护列

每个节点记录ll[6][6],rr[6][6],lr[6][6]分别表示区间左边界/右边界/左右边界点对之间只通过节点控制的区间的最短路

考虑合并两个区间

仿照bil125神,仿照bill125神,用ls表示左子树,rs表示右子树

UPD:少打了一个'l'被喷了T_T

lm[6][6]和rm[6][6]表示从左边界/右边界出发,到自己所在子树的另一边界必须经过另一子树且终点在子树中只有一个点的最短路

lm[i][j]=min(lm[i][j],ls.lr[i][k]+rs.ll[k][j]+ls.rr[j][j])

lm直观上是这样的,rm同理

【BZOJ】【P2104】【Wc2009】【shortest】【题解】【线段树】_第1张图片

计算完了lm,rm来更新父节点

a.ll[i][j]=min(a.ll[i][j],lm[i][k]+ls.lr[j][k]-ls.rr[k][k])

直观如下,rr同理

【BZOJ】【P2104】【Wc2009】【shortest】【题解】【线段树】_第2张图片

a.lr[i][j]=min(a.lr[i][j],ls.lr[i][k]+rs.lr[k][j]);
a.lr[i][j]=min(a.lr[i][j],lm[i][k]+rm[k][j]-ls.rr[k][k]-rs.ll[k][k]);

lr有两种可能,一种是直接从左往右穿过去,很简单不画了

另一种如下


正确性是因为行数只有6,最多拐3次弯

修改重构一条链

查询需要注意只用一个区间是不够的,需要1..l + l..r +r..n三个区间用上面的方法做

于是这题就是一个常数奇大无比的线段树

Code:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=1061109567;
int n;
int a[6][maxn];
struct node{
	int ll[6][6],rr[6][6],lr[6][6];
	void clear(){memset(ll,0x3f,sizeof ll);memset(rr,0x3f,sizeof rr);
				memset(lr,0x3f,sizeof lr);}
	bool empty()const{return ll[0][0]==inf;}	
}t[maxn<<2];
node operator+(const node &ls,const node &rs){
	static int lm[6][6],rm[6][6];
	if(ls.empty())return rs;if(rs.empty())return ls;
	node a;a.clear();
	memset(lm,0x3f,sizeof lm);memset(rm,0x3f,sizeof rm);
	for(int k=0;k<6;k++)for(int i=0;i<6;i++)for(int j=0;j<6;j++)
	lm[i][j]=min(lm[i][j],ls.lr[i][k]+rs.ll[k][j]+ls.rr[j][j]),
	rm[i][j]=min(rm[i][j],rs.ll[i][i]+ls.rr[i][k]+rs.lr[k][j]);
	for(int i=0;i<6;i++)
	for(int j=0;j<6;j++){
		a.ll[i][j]=ls.ll[i][j];a.rr[i][j]=rs.rr[i][j];a.lr[i][j]=inf;
		for(int k=0;k<6;k++){
			a.ll[i][j]=min(a.ll[i][j],lm[i][k]+ls.lr[j][k]-ls.rr[k][k]);
			a.rr[i][j]=min(a.rr[i][j],rm[k][i]+rs.lr[k][j]-rs.ll[k][k]);
			a.lr[i][j]=min(a.lr[i][j],ls.lr[i][k]+rs.lr[k][j]);
			a.lr[i][j]=min(a.lr[i][j],lm[i][k]+rm[k][j]-ls.rr[k][k]-rs.ll[k][k]);
		}
	}return a;
}
#define lson i<<1,l,(l+r)/2
#define rson i<<1|1,(l+r)/2+1,r
#define ls i<<1
#define rs i<<1|1
void init(int i,int l){
	int sum[6];
	for(int j=0;j<6;j++)sum[j]=(j?sum[j-1]:0)+a[j][l];
	for(int j=0;j<6;j++)for(int k=0;k<6;k++)
	t[i].ll[j][k]=t[i].rr[j][k]=t[i].lr[j][k]=sum[max(j,k)]-(min(j,k)?sum[min(j,k)-1]:0);
	return;
}
void build(int i,int l,int r){
	if(l==r){
		init(i,l);
		return;
	}build(lson);build(rson);
	t[i]=t[ls]+t[rs];
}
void Change(int i,int l,int r,int ps){
	if(l==r){init(i,l);return;}
	if(ps<=(l+r)/2)Change(lson,ps);
	else Change(rson,ps);
	t[i]=t[ls]+t[rs];
}
node Qmin(int i,int l,int r,int l0,int r0){
	if(l0<=l&&r0>=r)return t[i];
	node ans;ans.clear();
	if(l0<=(l+r)/2)ans=Qmin(lson,l0,r0)+ans;
	if(r0>(l+r)/2)ans=ans+Qmin(rson,l0,r0);
	return ans;
}	
#undef ls
#undef rs
int Qmin(int sx,int sy,int tx,int ty){
	node ls=Qmin(1,1,n,1,sy);
	node md=Qmin(1,1,n,sy,ty);
	node rs=Qmin(1,1,n,ty,n);
	int ans=md.lr[sx][tx];
	for(int i=0;i<6;i++)for(int j=0;j<6;j++){
		ans=min(ans,md.ll[sx][i]+ls.rr[i][j]+md.lr[j][tx]-a[i][sy]-a[j][sy]);
		ans=min(ans,md.lr[sx][i]+rs.ll[i][j]+md.rr[j][tx]-a[i][ty]-a[j][ty]);
		ans=min(ans,ls.rr[sx][i]+md.lr[i][j]+rs.ll[j][tx]-a[i][sy]-a[j][ty]);
	}return ans;
}
int getint(){
	int res=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c))res=res*10+c-'0',c=getchar();
	return res;
}
int main(){
	n=getint();
	for(int i=0;i<6;i++)
	for(int j=1;j<=n;j++)
	a[i][j]=getint();
	build(1,1,n);
	int m=getint();
	while(m--){
		int op=getint();
		if(op==1){
			int x=getint(),y=getint(),z=getint();
			a[x-1][y]=z;
			Change(1,1,n,y);
		}else{
			int x=getint(),y=getint(),z=getint(),w=getint();
			if(y>w){swap(x,z);swap(y,w);}
			printf("%d\n",Qmin(x-1,y,z-1,w));
		}
	}
	return 0;
}



你可能感兴趣的:(bzoj)