• 【GMOJ6293】迷宫


    前言

    为什么这群神仙跑的这么快。。。\(1200ms\)的我瑟瑟发抖\(qwq\)

    题目

    题目链接:https://gmoj.net/senior/#main/show/6293

    \(n\leq 5,m\leq 200000,Q\leq 50000\)

    思路

    先想一个暴力\(dp\)怎么做。设\(f[i][j][p][q]\)表示从第\(i\)\(p\)列到第\(j\)\(q\)列的最少步数。那么对于任意一个\(k(i\leq k\leq j)\),有

    \[f[i][j][p][q]=min^{m}_{l=1}(f[i][k][p][l]+f[k+1][j][l][q]+1) \]

    初始化是将每一列的任意两行的距离记录。
    显然,这个做法是针对无修改操作的一种最暴力的区间\(dp\)。而题目是要求支持修改的,容易发现这个转移式可以改成广义矩阵乘法,而且每次是询问区间的\(dp\)值,所以考虑用动态\(dp\)瞎搞。
    线段树维护区间矩阵乘法,线段树的每一个结点\([l,r]\)储存一个\(m\times m\)的矩阵,第\(i\)\(j\)列表示从网格图\(l\)\(i\)列走到\(r\)\(j\)列的最小路径长度。
    每次询问时间复杂度为\(O(m^3\log n)\),总时间复杂度为\(O(nm^3+Qm^3\log n)\)

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N=200010,M=6,Inf=1e9;
    int n,m,Q,opt,a[M][N];
    
    struct matrix
    {
    	int a[M][M];
    	
    	matrix()
    	{
    		memset(a,0x3f3f3f3f,sizeof(a));
    	}
    	
    	friend matrix operator *(matrix a,matrix b)
    	{
    		matrix c;
    		for (int i=1;i<M;i++)
    			for (int j=1;j<M;j++)
    				for (int k=1;k<M;k++)
    					c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]+1);
    		return c;
    	}
    }mat[N];
    
    struct SegTree
    {
    	int l[N*4],r[N*4];
    	matrix f[N*4];
    	
    	void pushup(int x)
    	{
    		f[x]=f[x*2]*f[x*2+1];
    	}
    	
    	void build(int x,int ql,int qr)
    	{
    		l[x]=ql; r[x]=qr;
    		if (ql==qr)
    		{
    			f[x]=mat[ql];
    			return;
    		}
    		int mid=(ql+qr)>>1;
    		build(x*2,ql,mid); build(x*2+1,mid+1,qr);
    		pushup(x);
    	}
    	
    	void update(int x,int k)
    	{
    		if (l[x]==k && r[x]==k)
    		{
    			f[x]=mat[l[x]];
    			return;
    		}
    		int mid=(l[x]+r[x])>>1;
    		if (k<=mid) update(x*2,k);
    			else update(x*2+1,k);
    		pushup(x);
    	}
    	
    	matrix ask(int x,int ql,int qr)
    	{
    		if (l[x]==ql && r[x]==qr) return f[x];
    		int mid=(l[x]+r[x])>>1;
    		if (qr<=mid) return ask(x*2,ql,qr);
    		if (ql>mid) return ask(x*2+1,ql,qr);
    		return ask(x*2,ql,mid)*ask(x*2+1,mid+1,qr);
    	}
    }seg;
    
    int main()
    {
    	freopen("maze.in","r",stdin);
    	freopen("maze.out","w",stdout);
    	scanf("%d%d%d",&m,&n,&Q);
    	for (int i=1;i<=m;i++)
    		for (int j=1;j<=n;j++)
    			scanf("%d",&a[i][j]);
    	for (int i=1;i<=n;i++)
    		for (int j=1;j<=m;j++)
    			for (int k=j;k<=m;k++)
    			{
    				bool flag=1;
    				for (int l=j;l<=k;l++)
    					if (!a[l][i]) flag=0;
    				if (flag) mat[i].a[j][k]=mat[i].a[k][j]=k-j;
    			}
    	seg.build(1,1,n);
    	while (Q--)
    	{
    		scanf("%d",&opt);
    		if (opt==1)
    		{
    			int x,i;
    			scanf("%d%d",&x,&i);
    			a[x][i]^=1;
    			memset(mat[i].a,0x3f3f3f3f,sizeof(mat[i].a));
    			for (int j=1;j<=m;j++)
    				for (int k=j;k<=m;k++)
    				{
    					bool flag=1;
    					for (int l=j;l<=k;l++)
    						if (!a[l][i]) flag=0;
    					if (flag) mat[i].a[j][k]=mat[i].a[k][j]=k-j;
    				}
    			seg.update(1,i);
    		}
    		else
    		{
    			int x,y,xx,yy;
    			scanf("%d%d%d%d",&x,&y,&xx,&yy);
    			matrix ans=seg.ask(1,y,yy);
    			if (ans.a[x][xx]<Inf) printf("%d\n",ans.a[x][xx]);
    				else printf("-1\n");
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux Core Dump【转】
    定位多线程内存越界问题实践总结【转】
    gcc __attribute__关键字举例之visibility【转】
    va_start(),va_end()函数应用【转】
    谁在call我-backtrace的实现原理【转】
    利用backtrace和ucontex定位segment错误【转】
    善用backtrace解决大问题【转】
    linux backtrace()详细使用说明,分析Segmentation fault【转】
    利用gcc自带的功能-fstack-protector检测栈溢出及其实现【转】
    Linux环境下段错误的产生原因及调试方法小结【转】
  • 原文地址:https://www.cnblogs.com/stoorz/p/12316230.html
Copyright © 2020-2023  润新知