zoukankan      html  css  js  c++  java
  • 洛谷 P5897

    题面传送门

    首先注意到这次行数与列数不同阶,列数只有 (200),而行数高达 (5000),因此可以考虑以行为下标建线段树,线段树上每个区间 ([l,r]) 开一个 (200 imes 200) 的数组 (d_{i,j}) 表示从 ((l,i))((r,j)) 的最短路,合并暴力用类似 floyd 的方式进行转移,这样暴力时间复杂度是 (RC^3+mC^2log R+q),空间复杂度 (RC^2),其中 (m) 为修改次数,一脸无法通过,而且 TL ML 双开花(大雾。

    考虑优化这个暴力,由于线段树建树就有个 (R),因此这个 (R) 是去不掉的,要优化也只能把这个 (c^3) 优化掉。注意到一件事情,就是当我们固定上端点((i))时,随着 (j) 的增大,(d_{i,j}) 的决策点是单调不降的,因为如果出现某个 (d_{i,j}) 的决策点 (p) 小于 (d_{i,j+1}) 的决策点 (q),否则 (i o j)(i o j+1) 的最短路径一定有个交点 (P),且这个 (P) 一定在 ([l,r]) 中后方((>dfrac{l+r}{2})),因此 (i o j)(i o j+1) 这两个最优路径在 (i o P) 的部分不重合,如果这两段长度相等那么我们可以把它们交换一下,答案不变却满足决策点单调性,否则我们完全可以把长度小的一段并到长度大的一段上去,答案更优。对于固定下断点的情况也同理,因此假设 (p_{i,j})(d_{i,j}) 的决策点,那么有 (p_{i-1,j}le p_{i,j}le p_{i,j+1}),决策单调性优化一下可以搞到 (c^2)

    这样 TL 倒是 ok 了,可 ML 还是会炸,这里又有一个 trick,我们设一个阈值 (B) 当区间长度很小,不超过 (B) 时候,直接暴力枚举起点 (dp) 求出最短路径即可,这样暴力 DP 复杂度是 (BC^2) 的,而我们建树时会 DP (dfrac{R}{B}) 次,每次修改都会 DP 一次,因此空间复杂度是 (dfrac{R}{B}C^2),时间复杂度 (RC^3+mC^2log R+q+mBC^2),刚好卡过去。

    据说这东西有个名字叫线段树分块?长见识了(

    const int MAXN=5e3;
    const int MAXC=200;
    const int MAXP=MAXN*4/20;
    const int INF=0x3f3f3f3f;
    int n,m,hor[MAXN+5][MAXC+5],ver[MAXN+5][MAXC+5];
    struct value{
    	int a[MAXC+5][MAXC+5];
    	value(){memset(a,63,sizeof(a));}
    };
    int p[MAXC+5][MAXC+5];
    value merge(value a,value b,int vx){
    	value c;memset(p,0,sizeof(p));
    	for(int i=1;i<=m;i++) for(int j=m;j;j--){
    		for(int k=((p[i-1][j])?(p[i-1][j]):1);k<=((p[i][j+1])?p[i][j+1]:m);k++)
    			if(c.a[i][j]>a.a[i][k]+b.a[k][j]+ver[vx][k])
    				c.a[i][j]=a.a[i][k]+b.a[k][j]+ver[vx][k],p[i][j]=k;
    	} return c;
    }
    struct node{int l,r;value v;} s[MAXP+5];
    int dp[MAXN+5][MAXC+5];
    value get(int u,int d){
    	value res;
    	for(int i=1;i<=m;i++){
    		for(int j=u;j<=d;j++) for(int k=1;k<=m;k++) dp[j][k]=INF;
    		dp[u][i]=0;
    		for(int j=u;j<=d;j++){
    			if(j!=u) for(int k=1;k<=m;k++) dp[j][k]=dp[j-1][k]+ver[j-1][k];
    			for(int k=2;k<=m;k++) chkmin(dp[j][k],dp[j][k-1]+hor[j][k-1]);
    			for(int k=m-1;k;k--) chkmin(dp[j][k],dp[j][k+1]+hor[j][k]);
    		} 
    //		for(int j=u;j<=d;j++) for(int k=1;k<=m;k++) printf("%d%c",dp[j][k]," 
    "[k==m]);
    		for(int j=1;j<=m;j++) res.a[i][j]=dp[d][j];
    	} return res;
    }
    void build(int k,int l,int r){
    	s[k].l=l;s[k].r=r;if(r-l<=20) return s[k].v=get(l,r),void();
    	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    	s[k].v=merge(s[k<<1].v,s[k<<1|1].v,mid);
    }
    void modify(int k,int x){
    	if(s[k].r-s[k].l<=20) return s[k].v=get(s[k].l,s[k].r),void();int mid=s[k].l+s[k].r>>1;
    	(x<=mid)?modify(k<<1,x):modify(k<<1|1,x);s[k].v=merge(s[k<<1].v,s[k<<1|1].v,mid);
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++) for(int j=1;j<m;j++) scanf("%d",&hor[i][j]);
    	for(int i=1;i<n;i++) for(int j=1;j<=m;j++) scanf("%d",&ver[i][j]);
    	build(1,1,n);int qu;scanf("%d",&qu);
    	while(qu--){
    		int opt;scanf("%d",&opt);
    		if(opt==1){
    			int x,y,z;scanf("%d%d%d",&x,&y,&z);
    			++x;++y;hor[x][y]=z;modify(1,x);
    		} else if(opt==2){
    			int x,y,z;scanf("%d%d%d",&x,&y,&z);
    			++x;++y;ver[x][y]=z;modify(1,x);
    		} else {
    			int x,y;scanf("%d%d",&x,&y);++x;++y;
    			printf("%d
    ",s[1].v.a[x][y]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    用.NET读取Flash格式文件信息
    通过ASP.NET页面重启服务器
    Webservice优点与缺点
    iClient 6R for Flex移动端开发的Q&A
    GIS十年路
    脚本调用命令行
    FME的简单介绍
    GIS从信息化到领域化(一)
    GIS从信息化到领域化(二)
    理性的看待地理信息共享交换平台建设
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P5897.html
Copyright © 2011-2022 走看看