jzoj100048 【NOIP2017提高A组模拟7.14】紧急撤离 (网格图,分治,bitSet)

来源:互联网 发布:熊掌好吃吗 知乎 编辑:程序博客网 时间:2024/06/05 03:07

题意

给出一个n*m <=500*500的网格图,有一些点有障碍。从一个点只能向下,向右走,询问q<=6105:(a,b)是否能到达(c,d)。

分析

询问这么多,如果不是并查集的话一定是预处理了。
考虑直接做,对于每一个询问要nm的时间来暴力。
优化?
枚举一个中间点,然后向两边寻找。
如何配对询问?
将询问用边集数组之类的打到点上。再用O(q)时间询问。
这样我们就切掉了这题 才怪咧,一个点的时间是O(n2),整张图是多少?

等等
不一定经过定点
但是在那一列两侧的一定经过那一列!
考虑分治
将从列mid切开,如果不经过中间的就左右两个区间分治搞。
经过中间的如何计算?
暴力还是n3的,考虑dp。发现dp也可以求出一个点(x,y)能到中间点的哪些点。
O(n3)
但如果dp使用bitset压位优化,时间就可以除掉一个常数32。
T(m)=2T(m2)+n2m/32
这样分下去可以分log m次,不难看出每一层都是n2m/32,所以最后时间复杂度就是O(n2m logm /32+q),也就是O(n3+q)。 从这里可以发现如果不用bitset那么时间就会多一个log,会T得飞起。
询问需要保证只会被访问一次,所以插入边集数组时有排序姿势。

CODE

#include <iostream>#include <cstdio>#include <bitset>#include <cstring>#include <algorithm>#define N 510using namespace std;bitset<N> bs[N][N];int n,m,q,ans[600001],tot;int next[600001],head[N][N];bool map[N][N];struct req{    int lx,ly,rx,ry,to;} a[600001],re[600001];bool cmp(req x,req y) {return x.ry<y.ry;}void link(int x,int y,const req &r) {    re[++tot]=r; next[tot]=head[x][y]; head[x][y]=tot;}void init() {    cin>>n>>m;    char c;    for (int i=1; i<=n; i++) {        scanf("\n");        for (int j=1; j<=m; j++) {            c=getchar(); map[i][j]=1-(c-'0');        }    }    cin>>q;    for (int i=1; i<=q; i++) scanf("%d %d %d %d",&a[i].lx,&a[i].ly,&a[i].rx,&a[i].ry),a[i].to=i;    sort(a+1,a+1+q,cmp);    for (int i=q; i; i--) link(a[i].lx,a[i].ly,a[i]);}void divide(int l,int r) {    if (l>r) return; int mid=l+r>>1;    divide(l,mid-1);    divide(mid+1,r);    for (int i=1; i<=n; i++) for (int j=l; j<=r; j++) bs[i][j].reset();    for (int i=1; i<=n; i++) if (map[i][mid]) bs[i][mid].set(i);    for (int j=mid; j>=l; j--) for (int i=n; i; i--)         if (map[i][j]) bs[i][j]|=bs[i+1][j] | bs[i][j+1];    for (int i=1; i<=n; i++) {        bs[i][mid].reset();        if (map[i][mid]) bs[i][mid].set(i);    }    for (int j=mid; j<=r; j++) for (int i=1; i<=n; i++)        if (map[i][j]) {            bs[i][j]|=bs[i-1][j];            if (j!=mid) bs[i][j]|=bs[i][j-1];        }    for (int i=1; i<=n; i++) for (int j=l; j<=mid; j++) {        for (int z=head[i][j]; z; z=next[z]) {            if (re[z].ry>r) break;            head[i][j]=next[z];            if (re[z].ry==re[z].ly) ans[re[z].to]=bs[re[z].rx][re[z].ry][re[z].lx];            else if (re[z].ry==mid) ans[re[z].to]=bs[i][j][re[z].rx];            else if (j==mid) ans[re[z].to]=bs[re[z].rx][re[z].ry][re[z].lx];            else if ((bs[i][j] & bs[re[z].rx][re[z].ry]).any()) ans[re[z].to]=1;        }    }}int main() {    freopen("3.in","r",stdin);    freopen("3.out","w",stdout);    init();    divide(1,m);    for (int i=1; i<=q; i++) if (ans[i]) printf("Safe\n"); else printf("Dangerous\n");}
阅读全文
2 0