JZOJ 100048. 【NOIP2017提高A组模拟7.14】紧急撤离(分治+状压DP)

JZOJ 100048. 【NOIP2017提高A组模拟7.14】紧急撤离

题目

Description

某日, 敌军对某村落展开攻击,所幸我情报部门提前预知了消息,村民兵武装连夜组织村民快速转移,为此他们需要赶往地道入口。已知村庄形成了 N * M 的方格网络,周围被封锁,无法穿行。其中有些方格没有敌军占领,可以进入,有些方格已经被敌军渗透,不能进入。由于敌军的步步紧逼,民众只能向行或列增大的地方移动:即(x, y) → (x + 1, y)或(x, y) → (x, y + 1)。 机智的 Star 手提笔记本,正和民兵队长商议对策。民兵队长会问你 Q 个问 题,每个问题均类似于坐标(a, b)的村民能否安全到达位于坐标(c, d)的地道(队长是机智的,他的问题总保证起点和终点均安全),你需要赶快写出程序来帮助他。

Input

第 1 行两个整数 N, M;
第 2 ~ n + 1 行给出一个 N * M 的 0/1 矩形(每行可看作一字符串),其中 0 表示安全,1 表示不安全,设第 i + 1 行第 j 列的位置坐标为(i, j)。
第 n + 2 行一个整数 Q;
接下来 Q 行每行四个整数 a、b、c、d,保证坐标合法。

Output

对于每组询问,输出一行“Safe”或“Dangerous”。

Sample Input

7 7
0100000
0000010
0001010
0011000
1000010
0000000
0111001
5
3 3 4 6
6 1 6 7
4 5 6 5
3 3 4 5
4 6 7 6

Sample Output

Dangerous
Safe
Safe
Dangerous
Dangerous

Data Constraint

对于 30%的数据,n, m ≤ 50;
对于另外 20%的数据,n, m ≤ 200,q ≤ 1000;
对于 100%的数据,n, m ≤ 500,q ≤ 600000。

题解

因为询问数过大,显然不能一个个求答案。
对于每组询问,从起点到终点的路径可能较长,所以考虑分治。
对列进行分治。
当前的区间 [ l , r ] [l,r] [l,r],取中间值 m i d mid mid
f [ i ] [ j ] [ k ] = 0 / 1 f[i][j][k]=0/1 f[i][j][k]=0/1为点 ( i , j ) (i,j) (i,j)是否能走到点 ( k , m i d ) (k,mid) (k,mid)
g [ i ] [ j ] [ k ] = 0 / 1 g[i][j][k]=0/1 g[i][j][k]=0/1为点 ( k , m i d ) (k,mid) (k,mid)是否能走到点 ( i , j ) (i,j) (i,j)
简单循环实现动态规划。
由于这样时间复杂度稍大,需要优化。
f , g f,g f,g数组一个位置可以压缩多个状态,可以压 30 30 30位(二进制十进制),在转移时直接通过“或”运算(某一位上同假为假,否则为真)即可实现时间的优化。
注意 j = m i d j=mid j=mid时要特殊处理。

算答案时分两种情况讨论:
一、起点和终点分别在 m i d mid mid列的两边(或在 m i d mid mid列上)
如果 ( a , b ) (a,b) (a,b)能走到 m i d mid mid列的某一位置,且该位置可以走到 ( c , d ) (c,d) (c,d),那么就是可以的。
用已算好的 f , g f,g f,g数组,通过“与”运算(某一位上同真为真,否则为假)即可快速实现。
二、起点和终点分别在 m i d mid mid列的同一边
继续分治 [ l , m i d − 1 ] [l,mid-1] [l,mid1] [ m i d + 1 , r ] [mid+1,r] [mid+1,r],这样每组询问总会有机会计算到。

这样我们先把询问排序,即可快速实现上述的分类。
还需要加以个优化,因为每组询问可能被多个 m i d mid mid计算到,所以可以每计算一组询问就删去,用双向链表维护。

代码

#include<cstdio>
#include<cstring>
using namespace std;
int a[510][510],n,m,ans[600010],h[510],e[31];
struct
{
	int a,b,c,d,id,l,r;
}b[600010];
int f[510][510][18],g[510][510][18];
void qsort(int l,int r)
{
	int x=l,y=r,mid=b[(l+r)/2].b;
	while(x<=y)
	{
		while(b[x].b<mid) x++;
		while(b[y].b>mid) y--;
		if(x<=y) b[0]=b[x],b[x]=b[y],b[y]=b[0],x++,y--;
	}
	if(x<r) qsort(x,r);
	if(y>l) qsort(l,y);
}
void dfs(int l,int r)
{
	int mid=(l+r)/2,i,j,k;
	for(i=1;i<=n;i++)
		for(j=l;j<=mid;j++) 
			for(k=1;k<=n/30+1;k++) f[i][j][k]=0;
	for(i=1;i<=n;i++)
		for(j=mid;j<=r;j++) 
			for(k=1;k<=n/30+1;k++) g[i][j][k]=0;
	for(i=n;i>0;i--) if(!a[i][mid]) 
	{
		k=(i-1)/30+1;int k1=(i-1)%30;
		f[i][mid][k]+=e[k1];
		for(j=k;j<=n/30+1;j++) 
			f[i][mid][j]=f[i][mid][j]|f[i+1][mid][j];
	}
	for(i=1;i<=n;i++) if(!a[i][mid]) 
	{
		k=(i-1)/30+1;int k1=(i-1)%30;
		g[i][mid][k]+=e[k1];
		for(j=1;j<=k;j++) 
			g[i][mid][j]=g[i][mid][j]|g[i-1][mid][j];
	}
	for(i=n;i>0;i--)
		for(j=mid-1;j>=l;j--) if(!a[i][j]) 
			for(k=1;k<=n/30+1;k++) 
			{
				f[i][j][k]=f[i+1][j][k]|f[i][j+1][k];
			} 
	for(i=1;i<=n;i++)
		for(j=mid+1;j<=r;j++) if(!a[i][j]) 
			for(k=1;k<=n/30+1;k++) 
			{
				g[i][j][k]=g[i-1][j][k]|g[i][j-1][k];
			}
	for(i=h[mid];i>0;i=b[i].l) if(ans[b[i].id]==-1)
	{
		if(b[i].d<mid) continue;
		ans[b[i].id]=0;
		for(k=1;k<=n/30+1;k++) if(f[b[i].a][b[i].b][k]&g[b[i].c][b[i].d][k]) ans[b[i].id]=1;
		b[b[i].l].r=b[i].r;
		b[b[i].r].l=b[i].l;
	}
	if(l<mid) dfs(l,mid-1);
	if(mid<r) dfs(mid+1,r);
}
int main()
{
	int i,j,q;
	char c;
	scanf("%d%d\n",&n,&m);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			scanf("%c",&c);
			a[i][j]=c-'0';
		}
		scanf("\n");
	}
	scanf("%d",&q);
	for(i=1;i<=q;i++) scanf("%d%d%d%d",&b[i].a,&b[i].b,&b[i].c,&b[i].d),b[i].id=i,ans[i]=-1;
	qsort(1,q);
	b[0].r=1,b[q+1].l=q;
	for(i=1;i<=q;i++) b[i].l=i-1,b[i].r=i+1;
	j=1;
	for(i=1;i<=m;i++)
	{
		h[i]=h[i-1];
		while(j<=q&&b[j].b<=i) h[i]=j,j++;
	}
	e[0]=1;for(i=1;i<=30;i++) e[i]=e[i-1]*2;
	dfs(1,m);
	for(i=1;i<=q;i++) if(ans[i]) printf("Safe\n"); else printf("Dangerous\n");
	return 0;
}
posted @ 2018-12-17 21:57  AnAn_119  阅读(52)  评论(0编辑  收藏  举报