【NOI2016】网格,离散化+求割点

思路:
蛋疼的一道题
当时做同步赛的时候这道题爆零了= =
%%%TA爷现场AC
连c=0的情况也没写
用到的算法很简单,离散化+求割点(然而当时的我并不会求割点= =)
但是写起来有很多要注意的细节问题
先说一下总体做法
很容易想到答案就是-1,0,1,2(显然,当时的我就算没学割点也看出来了……)
考虑分开讨论
-1
nmk<=1 或( nmk=2 且连在一起)
0
nmk>=2 且至少存在两个联通块
1
只有一个联通块,但有割点
2
除上面三种情况以外
情况看起来也很简单啊
但是实现起来并不容易
因为 n ,m太大所以要离散化建图
怎么建呢?
所有蛐蛐周围一圈的八个点
一开始我想的是建周围两圈,即24个点
然后检查每个跳蚤往其向下和向右碰到的第一个点,如果也是跳蚤就连边(这个可以分别按照x,y的关键字优先级排序来做)
然后dfs一遍,检查是否连通及是否存在割点即可
但如果仅仅这样建图,下面这个样例是无法处理的

5 5 1
3 5

画出来是这样的
这里写图片描述
如果如上建图, 5×5 的网格就变成了 3×3 的网格,原本不存在割点,而现在又有了
那怎么办呢?
我们可以把这个图再扩大一圈
也就是说检查我们所离散的点是否已经到达网格的边界,如果没有到达,就在他们周围在加一圈
比如上述这个样例错误的离散化是这样的
这里写图片描述
(因为这样就出来割点了)
正确的离散化
这里写图片描述
(实际上就是把最小的x,y-1,最大的x,y+1,注意不能超过[1,n],[1,m]的范围)
这样建好图以后就可以跑tarjan求割点了
注意数组范围的大小
每个蝈蝈最多可以扩展出8个点,而且离散后的x,y坐标为 1 ~3×105,在周围再扩充一层点的个数是 1.2×106
这样总点数大约是 2×106
每个点最多会向外连4条边,所以边数为 8×106
在UOJ上刷了一页的97pts
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define M 100005
#define LL long long
using namespace std;
int T,n,m,k,ID,cnt,tot,lef,rit,up,down;
bool any_cut,key;
struct node{
    int id,x,y;
    bool operator <(const node other)const
    {
        if (key)    
            if (x==other.x) return y<other.y;
            else return x<other.x;
        else
            if (y==other.y) return x<other.x;
            else return y<other.y;
    }
    bool operator ==(const node other)const
    {
        return x==other.x&&y==other.y;
    }
}a[M],b[M*25];
int X[M*3],Y[M*3],first[M*25],low[M*25],dfn[M*25];
struct edge{
    int v,next;
}e[M*90];
int in()
{
    int t=0;char ch=getchar();
    while (ch>'9'||ch<'0') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
void add(int x,int y)
{
    e[++tot].v=y;e[tot].next=first[x];first[x]=tot;
    e[++tot].v=x;e[tot].next=first[y];first[y]=tot;
}
void dfs(int x,int fa)
{
    dfn[x]=low[x]=++dfn[0];
    int child=0;
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=fa)
            if (!dfn[e[i].v])
            {
                child++;
                dfs(e[i].v,x);
                low[x]=min(low[x],low[e[i].v]);
                if (fa&&dfn[x]<=low[e[i].v])
                    any_cut=1;
                if (!fa&&child>=2)
                    any_cut=1;
            }
            else
                low[x]=min(low[x],dfn[e[i].v]);
}
void work()
{
    n=in();m=in();k=in();
    X[0]=0;Y[0]=0;cnt=0;ID=0;tot=0;any_cut=0;
    e[1].v=0;
    for (int x,y,i=1;i<=k;++i)
    {
        x=in();y=in();
        a[i]=(node){0,x,y};
        if (x>1) X[++X[0]]=x-1,b[++ID]=(node){0,x-1,y};
        if (x<n) X[++X[0]]=x+1,b[++ID]=(node){0,x+1,y};
        X[++X[0]]=x;
        if (y>1) Y[++Y[0]]=y-1,b[++ID]=(node){0,x,y-1};
        if (y<m) Y[++Y[0]]=y+1,b[++ID]=(node){0,x,y+1};
        Y[++Y[0]]=y;    
        if (x>1&&y>1) b[++ID]=(node){0,x-1,y-1};
        if (x>1&&y<m) b[++ID]=(node){0,x-1,y+1};
        if (x<n&&y>1) b[++ID]=(node){0,x+1,y-1};
        if (x<n&&y<m) b[++ID]=(node){0,x+1,y+1};
    }
    if ((LL)n*m-k<=1) return void(puts("-1"));
    memset(dfn,0,sizeof(dfn)); 
    memset(low,0,sizeof(low));
    memset(first,0,sizeof(first));
    up=lef=M,down=rit=0;
    for (int i=1;i<=X[0];++i)
        lef=min(X[i],lef),
        rit=max(X[i],rit);
    for (int i=1;i<=Y[0];++i)
        up=min(Y[i],up),
        down=max(Y[i],down);
    if (lef>1) --lef;
    if (rit<n) ++rit;
    if (up>1) --up;
    if (down<m) ++down;
    X[++X[0]]=lef;X[++X[0]]=rit;
    Y[++Y[0]]=up;Y[++Y[0]]=down;
    sort(X+1,X+X[0]+1);
    X[0]=unique(X+1,X+X[0]+1)-X-1;
    sort(Y+1,Y+Y[0]+1);
    Y[0]=unique(Y+1,Y+Y[0]+1)-Y-1;
    for (int i=1;i<=X[0];++i)
        b[++ID]=(node){0,X[i],Y[1]},
        b[++ID]=(node){0,X[i],Y[Y[0]]};
    for (int i=1;i<=Y[0];++i)
        b[++ID]=(node){0,X[1],Y[i]},
        b[++ID]=(node){0,X[X[0]],Y[i]};
    key=0;
    sort(a+1,a+k+1);
    sort(b+1,b+ID+1);
    ID=unique(b+1,b+ID+1)-b-1;
    for (int i=1;i<=k;++i)
        a[i].x=lower_bound(X+1,X+X[0]+1,a[i].x)-X,
        a[i].y=lower_bound(Y+1,Y+Y[0]+1,a[i].y)-Y;
    for (int i=1;i<=ID;++i)
        b[i].x=lower_bound(X+1,X+X[0]+1,b[i].x)-X,
        b[i].y=lower_bound(Y+1,Y+Y[0]+1,b[i].y)-Y,
        b[i].id=i;
    for (int t=1,tt=1,i=1;i<=ID;++i)
    {
        node tmp=b[i];
        while (t<=k&&a[t]<tmp) ++t;
        if (t>=1&&t<=k&&a[t]==tmp) continue;
        ++cnt;
        tmp.x++;
        while (t<=k&&a[t]<tmp) ++t;
        while (tt<=ID&&b[tt]<tmp) ++tt;
        if (tt>=1&&tt<=ID&&b[tt].y==b[i].y&&b[tt].x>b[i].x&&(t<1||t>k||b[tt]<a[t]))
            add(b[i].id,b[tt].id);
    }
    key=1;
    sort(a+1,a+k+1);
    sort(b+1,b+ID+1);
    for (int t=1,tt=1,i=1;i<=ID;++i)
    {
        node tmp=b[i];
        while (t<=k&&a[t]<tmp) ++t;
        if (t>=1&&t<=k&&a[t]==tmp) continue;
        tmp.y++;
        while (t<=k&&a[t]<tmp) ++t;
        while (tt<=ID&&b[tt]<tmp) ++tt;
        if (tt>=1&&tt<=ID&&b[tt].x==b[i].x&&b[tt].y>b[i].y&&(t<1||t>k||b[tt]<a[t]))
            add(b[i].id,b[tt].id);
    }
    if (e[1].v) dfs(e[1].v,0);
    if ((LL)n*m-k==2&&(dfn[0]==2||!k)) return void(puts("-1"));
    if ((LL)n*m-k>=2&&dfn[0]<cnt) return void(puts("0"));
    if (dfn[0]==cnt&&any_cut) return void(puts("1"));
    if (n==1||m==1) return void(puts("1"));
    puts("2");
}
main()
{
    for (T=in();T;--T) work();
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值