zoukankan      html  css  js  c++  java
  • 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,&m);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			scanf("%c",&c);
    			a[i][j]=c-'0';
    		}
    		scanf("
    ");
    	}
    	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
    "); else printf("Dangerous
    ");
    	return 0;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    leetcode—Swap Nodes in Pairs
    leetcode--Merge k Sorted Lists
    leetcode—Valid Parentheses
    leetcode—3sum
    编程工具(保持更新)
    QT下调用摄像头(opencv2.4.4)
    qtcreator 与 opencv
    在线公式编辑器
    QT学习(对话框)codeblock版本
    QT-opencv小结
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910101.html
Copyright © 2011-2022 走看看