LA 4082 Traffic Jam (分块)

题意: 有一个两行C列的城市网格。每个城市用(r,c)表示,即第r行的第c列。一共有3*C-2条道路,用两端的城市坐标(r1,c1)-(r2,c2)表示。要求支持3种操作:打开某条边,关闭某条边,检查某两个点是否连通(即:只通过打开的边互相可达)。初始时所有边关闭。

解法: 如果没有关闭边的操作,那么只需要并查集即可,但是因为需要删边,使得并查集无法使用,但是路径压缩的思想,在这题仍然还是可用的。把C列分成C/size个长度为size的块,每次对边操作时,重新计算边所属块的连通性(即块最左边的两个点与块最右边的两个点是否可达),然后询问时,对于询问点所属的块,暴力计算该点和该块左右端点的连通性,再通过之前处理的块的信息来计算该点和全图中其他块的连通性,若询问的两个点的访问集合有交集,即说明他们互相可达。注意这里必须是计算点到全图所有块的连通性,想象一下你要去街道的对面,是不是需要先找到斑马线,而斑马线可能距离你的位置很远。还有注意有两个点坐标相同的数据,这种直接就是Y。

代码细节真多。

/* Created Time: Wednesday, October 30, 2013 AM09:01:53 CST */
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
using namespace std;
const int M = 100100;
int lx,ly,rx,ry,n;
int b_size,b_num,g[2][M],block[555][2][2],tim,vis[2][M];
void init() {
    memset(g,0,sizeof(g));
    b_size = int(sqrt(double(n)));
    b_num = n/b_size + (n%b_size!=0);
    memset(block,0,sizeof(block));
    memset(vis,0,sizeof(vis));
    tim = 0;
}
void get(int x,int y) {
    int bb = y/b_size;
    int ly = bb*b_size;
    int ry = min(ly+b_size,n)-1;
    tim ++;
    vis[0][ly] = tim;
    if (g[0][ly]&4) vis[1][ly] = tim;
    for (int i = ly+1; i<=ry; i ++) {
        if (vis[0][i-1]==tim && (g[0][i]&8)) {
            vis[0][i] = tim;
            if (g[0][i]&4) vis[1][i] = tim;
        }
        if (vis[1][i-1]==tim && (g[1][i]&8)) {
            vis[1][i] = tim;
            if (g[1][i]&1) vis[0][i] = tim;
        }
    }
    block[bb][0][0] = (vis[0][ry]==tim);
    block[bb][0][1] = (vis[1][ry]==tim);

    tim ++;
    vis[1][ly] = tim;
    if (g[1][ly]&1) vis[0][ly] = tim;
    for (int i = ly+1; i<=ry; i ++) {
        if (vis[0][i-1]==tim && (g[0][i]&8)) {
            vis[0][i] = tim;
            if (g[0][i]&4) vis[1][i] = tim;
        }
        if (vis[1][i-1]==tim && (g[1][i]&8)) {
            vis[1][i] = tim;
            if (g[1][i]&1) vis[0][i] = tim;
        }
    }
    block[bb][1][0] = (vis[0][ry]==tim);
    block[bb][1][1] = (vis[1][ry]==tim);

}
void update() {
    if (lx==rx) {
        if (ly>ry) swap(ly,ry);
        g[lx][ly] ^= 2;
        g[rx][ry] ^= 8;
    } else {
        if (lx>rx) swap(lx,rx);
        g[lx][ly] ^= 4;
        g[rx][ry] ^= 1;
    }
    get(lx,ly);
    get(rx,ry);
}
bool flag;
void mark(int x,int y) {
    if (vis[x][y]==tim-1) flag = true;
    vis[x][y] = tim;
}
bool move(int x,int y) {
    flag = false;
    tim ++;
    vis[x][y] = tim;
    if (x==0 && (g[x][y]&4)) mark(!x,y);
    if (x==1 && (g[x][y]&1)) mark(!x,y);
    int ty,bb;
    bb = y/b_size;
    ty = min((bb+1)*b_size,n)-1;
    for (int i = y+1; i <= ty; i ++) {
        if (vis[0][i-1]==tim && (g[0][i]&8)) {
            mark(0,i);
            if (g[0][i]&4) mark(1,i);
        }
        if (vis[1][i-1]==tim && (g[1][i]&8)) {
            mark(1,i);
            if (g[1][i]&1) mark(0,i);
        }
    }
    for (int i = bb+1; i < b_num; i ++) {
        int py = i*b_size-1;
        if (vis[0][py]==tim && (g[0][py]&2)) {
            mark(0,py+1);
            if (g[0][py+1]&4) mark(1,py+1);
        }
        if (vis[1][py]==tim && (g[1][py]&2)) {
            mark(1,py+1);
            if (g[1][py+1]&1) mark(0,py+1);
        }
        int ty = min(py+b_size,n-1);
        if (vis[0][py+1]==tim) {
            if (block[i][0][0]) mark(0,ty);
            if (block[i][0][1]) mark(1,ty);
        }
        if (vis[1][py+1]==tim) {
            if (block[i][1][0]) mark(0,ty);
            if (block[i][1][1]) mark(1,ty);
        }
    }
    
    ty = bb*b_size;
    for (int i = y-1; i >= ty; i --) {
        if (vis[0][i+1]==tim && (g[0][i]&2)) {
            mark(0,i);
            if (g[0][i]&4) mark(1,i);
        }
        if (vis[1][i+1]==tim && (g[1][i]&2)) {
            mark(1,i);
            if (g[1][i]&1) mark(0,i);
        }
    }
    for (int i = bb-1; i >= 0; i --) {
        int py = (i+1)*b_size-1;
        if (vis[0][py+1]==tim && (g[0][py]&2)) {
            mark(0,py);
            if (g[0][py]&4) mark(1,py);
        }
        if (vis[1][py+1]==tim && (g[1][py]&2)) {
            mark(1,py);
            if (g[1][py]&1) mark(0,py);
        }
        int ty = i*b_size;
        if (vis[0][py]==tim) {
            if (block[i][0][0]) mark(0,ty);
            if (block[i][1][0]) mark(1,ty);
        }
        if (vis[1][py]==tim) {
            if (block[i][0][1]) mark(0,ty);
            if (block[i][1][1]) mark(1,ty);
        }
    }
    return flag;
}
bool ask() {
    if (lx==rx && ly==ry) return true;
    move(lx,ly);
    return move(rx,ry);
}
int main() {
    int cas;
    char s[22];
    scanf("%d",&cas);
    while (cas--) {
        scanf("%d",&n);
        init();
        while (~scanf("%s",s)) {
            if (s[0]=='E') break;
            scanf("%d%d%d%d",&lx,&ly,&rx,&ry);
            lx--,ly--,rx--,ry--;
            if (s[0]=='A') 
                puts(ask()?"Y":"N");
            else
                update();
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值