• 洛谷 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;
    }
    
  • 相关阅读:
    【Android Developers Training】 73. 布局变化的动画
    【Android Developers Training】 72. 缩放一个视图
    【Android Developers Training】 71. 显示翻牌动画
    svn更改地址怎么办
    python学习手册
    failed to bind pixmap to texture
    Ubuntu 12.04安装Google Chrome
    svn update 时总是提示 Password for '默认密钥' GNOME keyring: 输入密码
    重设SVN 的GNOME keyring [(null)] 的密码
    Nginx + uWSGI + web.py 搭建示例
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P5897.html
Copyright © 2020-2023  润新知