bzoj 1018: [SHOI2008]堵塞的交通traffic

链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1018

理论上是可以直接线段树分治的,直接维护点的连通性,但这样是 l o g 2 log^2 log2
有更优秀的做法
我们考虑对于这种网格图,我们是可以分成两个部分来考虑连通性的
对于每一个部分,我们要维护的是左右边界两两之间的连通性,不难发现,设宽为n,那么状态数是 n 2 n^2 n2
至于部分之间的合并,我们可以枚举从哪一列走过去,然后用两边的左右边界连通性合并即可,
这里只给两列的意思就是让你方便讨论,这样就只有两个情况了,暴力讨论就好了
显然可以用线段树进行维护
因为支持合并,我们线段树还要维护在mid这个位置,两列的连通性如何
然后就没什么了
修改就暴力修改,询问的话要注意还要查询左右两边剩下的点再讨论一次,因为我们线段树查询的是区间,并不可以维护经过左右两边的情况
如果数据出得更大的话可以用一样的方法来做,但常数会变大,讨论应该可以用for代替

CODE:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=100005;
int C;
struct qq
{
    int L,R;//哪一个矩阵
    int U,D,l,r,u,d,q,p;
    int s1,s2;
}tr[N<<2];int num=0;
void bt (int l,int r)
{
    int a=++num;
    tr[a].L=l;tr[a].R=r;
    if (l==r)
    {
        tr[a].U=tr[a].D=tr[a].u=tr[a].d=1;
        return ;
    }
    int mid=(l+r)>>1;
    tr[a].s1=num+1;bt(l,mid);
    tr[a].s2=num+1;bt(mid+1,r);
}
/*U:第一行mid,mid+1两列之间是否联通
D:第二行mid,mid+1两列之间是否联通
l:s1,s3是否联通
r:s2,s4是否联通
u:s1,s2是否联通
d:s3,s4是否联通
q:s1,s4是否联通
p:s3,s2是否联通*/
void Merge (qq &now,qq s1,qq s2)
{
    now.l=s1.l|(s1.u&now.U&s2.l&now.D&s1.d);
    now.r=s2.r|(s2.u&now.U&s1.r&now.D&s2.d);
    now.u=(s1.u&now.U&s2.u)|(s1.q&now.D&s2.p);
    now.d=(s1.d&now.D&s2.d)|(s1.p&now.U&s2.q);
    now.q=(s1.u&now.U&s2.q)|(s1.q&now.D&s2.d);
    now.p=(s1.d&now.D&s2.p)|(s1.p&now.U&s2.u);
}
void update (int now,int x,int y,int z)//修改
{
 
    int l=tr[now].L,r=tr[now].R;     
//  printf("YES:%d %d %d %d %d\n",l,r,x,y,z);
    int mid=(l+r)>>1;
    int s1=tr[now].s1,s2=tr[now].s2;
    if (x==mid)//修改的刚刚好是中轴线
    {
        if (y==1) tr[now].U=z;
        else tr[now].D=z;
        Merge(tr[now],tr[s1],tr[s2]);
        return ;
    }
    if (x<=mid) update(s1,x,y,z);
    else update(s2,x,y,z);
    Merge(tr[now],tr[s1],tr[s2]);
}
void update1 (int now,int x,int z)
{
    int l=tr[now].L,r=tr[now].R;
    int mid=(l+r)>>1;
    int s1=tr[now].s1,s2=tr[now].s2;
    if (l==r)
    {
        tr[now].l=tr[now].r=tr[now].p=tr[now].q=z;
        return ;
    }
    if (x<=mid) update1(s1,x,z);
    else update1(s2,x,z);
    Merge(tr[now],tr[s1],tr[s2]);
}
qq get (int now,int x,int y)
{
    int l=tr[now].L,r=tr[now].R;
    int mid=(l+r)>>1;
    int s1=tr[now].s1,s2=tr[now].s2;
    if (x==l&&y==r) return tr[now];
    if (y<=mid) return get(s1,x,y);
    else if (x>mid) return get(s2,x,y);
    else
    {
        qq shen=tr[now];
        Merge(shen,get(s1,x,mid),get(s2,mid+1,y));
        return shen;
    }
}
int main()
{
    scanf("%d",&C);
    bt(1,C);
    while (true)
    {
        char ss[5];
        int r1,c1,r2,c2;
        scanf("%s",ss);
        if (ss[0]=='E') break;
        scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
        if (c1>c2) swap(c1,c2),swap(r1,r2);
        if (ss[0]=='A')
        {
            /*U:第一行mid,mid+1两列之间是否联通
                D:第二行mid,mid+1两列之间是否联通
                l:s1,s3是否联通
                r:s2,s4是否联通
                u:s1,s2是否联通
                d:s3,s4是否联通
                q:s1,s4是否联通
                p:s3,s2是否联通*/
            qq l=get(1,1,c1),x=get(1,c1,c2),r=get(1,c2,C);
            int ans=0;
            if (r1==1&&r2==1)//在同一行
                ans=x.u|(l.r&x.p)|(x.q&r.l)|(l.r&x.d&r.l);
            if (r1==1&&r2==2)//不在同一行
                ans=x.q|(l.r&x.d)|(x.u&r.l)|(l.r&x.p&r.l); 
            if (r1==2&&r2==1)
                ans=x.p|(l.r&x.u)|(x.d&r.l)|(l.r&x.q&r.l);
            if (r1==2&&r2==2)
                ans=x.d|(l.r&x.q)|(x.p&r.l)|(l.r&x.u&r.l);
            if (ans==1) printf("Y\n");
            else printf("N\n");
        }
        else//连通
        {
            if (r1==r2)//修改的横着的
                 update(1,c1,r1,ss[0]=='O');
            else//修改竖着的 
                update1(1,c1,ss[0]=='O');
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值