BZOJ4423: [AMPPZ2013]Bytehattan 对偶图+并查集

题目链接:

4423: [AMPPZ2013]Bytehattan

题意:

网格图每次删单位长度边之后询问边的两点是否仍然联通,强制在线。

思路:

本题的强制在线体现在下一次的输入里只有一个是正确的输入,哪个是取决于上一次得出的结果。

考虑如果不强制在线,可以把整个删边的过程反过来看,先把k次操作的删边都删掉,然后倒序每一次删边看作加边。如果加边之前两点不联通,而加边后两点联通了,说明删掉这条边会使这两点不联通。维护联通与否可以用路径压缩的并查集。倒序跑一次就可以得出每次操作的答案。

可是这道题难就难在了强制在线,一旦强制在线做法就完全不同了。

首先简单介绍一下对偶图的概念,对偶图是由平面图变来的,平面图的概念就是:图画在平面上,边的交点只能为结点的图。对偶图就是把边圈起来的一个个“网格”看作结点形成的图。就网格图而言,网格图里原来交叉点当作一个结点,对偶图里就是把白块当作一个结点。

对偶图

如上图中将3x3的网格外界看为一点,每个网格看成一点,它们构成一个无向图。

考虑删边什么时候会使两个点不连通,举个例子,(1,2)和(2,2),当(1,2)到(2,2)的唯一桥梁是它们之间直接相连的这条线段时,删边会使其不连通,此时这条线段左侧两条边和右侧两条边每部分一定至少各删掉了一条,0,1已经联通了,0,2也已经联通了。

总结归纳出当所删边两侧方格已经联通的时候,删掉这条边将使两点不联通。那么就可以将问题转化为网格图的对偶图删边之前两点是否联通。给每个网格编个号,用并查集维护是否连通,此题可解。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 2250000;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-6;
static const double pi = acos(-1);

void redirect(){
    #ifdef LOCAL
        freopen("test.txt","r",stdin);
    #endif
}

int fa[maxn];
int find(int x){
    return fa[x] == x ? x : (fa[x] = find(fa[x]));
}
void combine(int x, int y) {
    int xfa = find(x),yfa = find(y);
    if(xfa != yfa) {
        fa[yfa] = xfa;
    }
}
void init(){
    for(int i = 0;i < maxn;i++)fa[i] = i;
}
int n,a1,b1,a2,b2;
char c1,c2;
int solve(int a,int b,int type){
    int x,y;
    if(type){
        if(b == 1)x = 0,y = (a-1)*(n-1)+1;
        else if(b == n)x = 0,y = a*(n-1);
        else x = (a-1)*(n-1)+b-1,y = (a-1)*(n-1)+b;
    }
    else{
        if(a == 1)x = 0,y = b;
        else if(a == n)x = 0,y = (n-2)*(n-1)+b;
        else x = (a-2)*(n-1)+b,y = (a-1)*(n-1)+b;
    }
    if(find(x) == find(y)){
        puts("NIE");
        return 1;
    }
    puts("TAK");
    combine(x,y);
    return 0;
}
int main(){
    redirect(); 
    init();
    int k,type = 0;
    scanf("%d %d",&n,&k);
    for(int i = 1;i <= k;i++){
        scanf("%d %d %c",&a1,&b1,&c1);
        getchar();
        scanf("%d %d %c",&a2,&b2,&c2);
        getchar();
        if(type)type = solve(a2,b2,c2=='N'?0:1);
        else type = solve(a1,b1,c1=='N'?0:1);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值