• 「IOI2013」wombats


    题目链接

    题目大意

    (n imes m) 的网格图 这个网格图中不能往上走 要求支持如下操作:

    1. 修改网格图上的一条边
    2. 询问从第一行某个点到最后一行某个点的最短路径

    (n leq 5000,m leq 200,q leq 2 imes 10^5) 修改次数 (d leq 500)

    题解

    看到这个行+单点修改可以考虑维护一个线段树:令 (A_{i,j}) 表示当前块内上面第 (i) 个点到下面第 (j) 个点的最短距离,合并的时候是一个((min,+)) 的矩乘。这样是 (O(dm^3logn)) 的复杂度 不可能通过。

    优化 1

    考虑合并两块的时候 先固定起点 (i) 。设底行的某个点为 (j) ,与中间线上相交的点为 (k)。我们可以发现 随着 (j) 的增加 (k) 一定不会减少。于是这个有单调性 可以用类似于决策单调性分治的方法做 单次合并时间复杂度变成了 (O(m^2logm))可能卡常后足以通过此题

    优化 2

    [scode type="yellow"] 这可能是一个二维 dp 优化的常用套路[/scode]

    我们设 (p_{i,j}) 表示上层 (i) 到下层 (j) 经过的是哪个点。容易证明有 (p_{i-1,j} leq p_{i,j} leq p_{i,j+1})

    于是每次枚举第三维的时候只需要枚举 ([p_{i-1,j},p_{i,j+1}]) 就可以了。画个图发现每个 (p) 形如一个斜线 加起来近似是一个 (m imes m) 的正方形。所以复杂度是 (O(m^2)) 的。

    优化 3

    但是空间会爆炸怎么办呢?

    线段树爆空间 一种常见的处理方法是底层暴力。

    我们分块。块内暴力处理 每个块作为线段树的底层合并就可以。

    暴力可以用之前写好的矩乘方便实现。

    #include<bits/stdc++.h>
    
    #define fi first
    #define se second
    #define U unsigned
    #define P std::pair<int,int>
    #define LL long long
    #define pb push_back
    #define MP std::make_pair
    #define all(x) x.begin(),x.end()
    #define CLR(i,a) memset(i,a,sizeof(i))
    #define FOR(i,a,b) for(int i = a;i <= b;++i)
    #define ROF(i,a,b) for(int i = a;i >= b;--i)
    #define DEBUG(x) std::cerr << #x << '=' << x << std::endl
    
    const int MAXN = 5000+5;
    const int MAXM = 205+5;
    const int BLO = 16;
    int R[MAXN][MAXM],D[MAXN][MAXM];
    int n,m;
    int blo[MAXN];
    
    struct Matrix{
    	int a[MAXM][MAXM];
    	
    	friend Matrix operator * (const Matrix &A,const Matrix &B){
    		Matrix C;
    		static int s[MAXM][MAXM];
    		FOR(i,0,m+1) FOR(j,0,m+1) s[i][j] = -1;
    		FOR(i,1,m){
    			ROF(j,m,1){
    				int l = 1,r = m;
    				if(s[i-1][j] != -1) l = std::max(l,s[i-1][j]);
    				if(s[i][j+1] != -1) r = std::min(r,s[i][j+1]);
    				C.a[i][j] = 1e9;
    //				DEBUG(l);DEBUG(r);
    				FOR(k,l,r){
    					if(C.a[i][j] > A.a[i][k]+B.a[k][j]){
    						C.a[i][j] = A.a[i][k]+B.a[k][j];
    						s[i][j] = k;
    					}
    				}
    			}
    		}
    		return C;
    	}
    	
    	inline void debug(){
    		puts("--- NEW MESSAGE ---");
    		FOR(i,1,m) FOR(j,1,m) printf("%d%c",a[i][j]," 
    "[j==m]);
    		puts("--- END MESSAGE ---");
    	}
    }sm[(MAXN/BLO)<<2];
    
    inline Matrix gen(int x){// 生成第 x 行的矩阵
    	Matrix res;
    	FOR(i,1,m){
    		int sm = 0;
    		FOR(j,i,m){
    			res.a[i][j] = sm+D[x][j];
    			sm += R[x][j];
    		}
    		sm = 0;
    		ROF(j,i-1,1){
    			sm += R[x][j];
    			res.a[i][j] = sm+D[x][j];
    		}
    	}
    	return res;
    }
    
    inline Matrix rebuild(int x){// 重构第 x 块
    	int l = (x-1)*BLO+1,r = std::min(n,BLO*x);
    	Matrix res = gen(l);
    //	res.debug();
    	FOR(i,l+1,r){
    		res = res*gen(i);
    //		exit(0);
    //		res.debug();
    	}
    //	res.debug();
    	return res;
    }
    
    #define lc ((x)<<1)
    #define rc ((x)<<1|1)
    inline void build(int x,int l,int r){
    	if(l == r){
    		sm[x] = rebuild(l);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(lc,l,mid);build(rc,mid+1,r);
    	sm[x] = sm[lc]*sm[rc];
    }
    
    inline void update(int x,int l,int r,int p){
    	if(l == r){
    		sm[x] = rebuild(l);
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(p <= mid) update(lc,l,mid,p);
    	else update(rc,mid+1,r,p);
    	sm[x] = sm[lc]*sm[rc];
    }
    
    int main(){
    	scanf("%d%d",&n,&m);
    	FOR(i,1,n) blo[i] = 1+((i-1)/BLO);
    	FOR(i,1,n) FOR(j,1,m-1) scanf("%d",&R[i][j]);
    	FOR(i,1,n-1) FOR(j,1,m) scanf("%d",&D[i][j]);
    	build(1,1,blo[n]);
    //	sm[1].debug();
    	int q;scanf("%d",&q);
    	while(q--){
    		int opt;scanf("%d",&opt);
    		if(opt == 1){
    			int x,y,w;scanf("%d%d%d",&x,&y,&w);++x;++y;
    			R[x][y] = w;update(1,1,blo[n],blo[x]);
    		}
    		if(opt == 2){
    			int x,y,w;scanf("%d%d%d",&x,&y,&w);++x;++y;
    			D[x][y] = w;update(1,1,blo[n],blo[x]);
    		}
    		if(opt == 3){
    			int x,y;scanf("%d%d",&x,&y);
    			++x;++y;
    			printf("%d
    ",sm[1].a[x][y]);
    		}
    	}
    	return 0;
    }
    /*
    3 4
    0 2 5
    7 1 1
    0 4 0
    0 0 0 2
    0 3 4 7
    1
    3 2 1
    */
    
  • 相关阅读:
    java中级或者高级面试题分享
    java常使用的框架
    spring的定时器
    ArrayList源码理解
    缓存 Memached
    ORM框架
    Web处理方式
    git使用
    Entity Framework
    .net 学习笔记2
  • 原文地址:https://www.cnblogs.com/rainair/p/14471637.html
Copyright © 2020-2023  润新知