[SHOI2008]堵塞的交通traffic

Description
有一天,由于某种穿越现象作用,你来到了传说中的小人国。小人国的布局非常奇特,整个国家的交通系统可以被看成是一个 2 C列的矩形网格,网格上的每个点代表一个城市,相邻的城市之间有一条道路,所以总共有 2C 个城市和 3C2 条道路。 小人国的交通状况非常槽糕。有的时候由于交通堵塞,两座城市之间的道路会变得不连通,直到拥堵解决,道路才会恢复畅通。初来咋到的你决心毛遂自荐到交通部某份差事,部长听说你来自一个科技高度发达的世界,喜出望外地要求你编写一个查询应答系统,以挽救已经病入膏肓的小人国交通系统。 小人国的交通部将提供一些交通信息给你,你的任务是根据当前的交通情况回答查询的问题。交通信息可以分为以下几种格式:
Close r1 c1 r2 c2 :相邻的两座城市 (r1,c1) (r2,c2) 之间的道路被堵塞了;
Open r1 c1 r2 c2 :相邻的两座城市 (r1,c1) (r2,c2) 之间的道路被疏通了;
Ask r1 c1 r2 c2 :询问城市 (r1,c1) (r2,c2) 是否连通。如果存在一条路径使得这两条城市连通,则返回Y,否则返回N;

Input
第一行只有一个整数 C ,表示网格的列数。接下来若干行,每行为一条交通信息,以单独的一行“Exit”作为结束。我们假设在一开始所有的道路都是堵塞的。我们保证C小于等于 100000 ,信息条数小于等于 100000

Output
对于每个查询,输出一个“Y”或“N”。

Sample Input
2
Open 1 1 1 2
Open 1 2 2 2
Ask 1 1 2 2
Ask 2 1 2 2
Exit

Sample Output
Y
N

HINT
题解:JudgeOnline/upload/201604/sol(4).rar

Source

思路
首先对于一个只有一行的情况,可以很容易的用线段树维护。
但是对于两行的情况,就需要用一些奇怪的技巧了。
考虑下面这个图:
就是官方题解里的图
r1,1 r2,2 可以先从 r1,1 r2,1 ,再到 r1,2 ,最后是 r2,2
那么可以维护 r1,1 r2,1 的联通性, r2,1 r1,2 的联通性, r1,2 r2,2 的联通性。
具体一点,假设有区间为:

q ———r
z ———v

那么需要维护 q r z v这4个点两两之间的联通性。
那么更新时就通过这个更新,还有区间 mid mid+1 的联通性(第一列和第二列)就好了。
询问就按照上面的查询方法写。

代码
其实是可以不用写这么多的,只是我写的太长了……

#include <cstdio>
#include <algorithm>

const int maxn=100000;

struct data
{
  int we,xc,qr,zv,qz,rv,qv,rz;
  //解释一下变量名的含义:
  //将键盘上左手边的一块看成一个矩形
  //就是
  //qwer
  //asdf
  //zxcv
  //把q,r,z,v看成矩形的4个顶点
  //w和x看成是中间点
  //e和c看成是中间点的编号+1位置的点
  //连起来写的两个字符表示两个位置的联通性
};

struct segment_tree
{
  data val[(maxn<<2)+10];

  inline int merge(data &d,data l,data r)
  //精华部分,自己画个图就明白了
  //意思是将l和r区间合并成一个区间d
  {
    d.qz=l.qz|(l.qr&d.we&r.qz&d.xc&l.zv);
    d.rv=r.rv|(r.qr&d.we&l.rv&d.xc&r.zv);
    d.qr=(l.qv&d.xc&r.rz)|(l.qr&d.we&r.qr);
    d.zv=(l.zv&d.xc&r.zv)|(l.rz&d.we&r.qv);
    d.qv=(l.qv&d.xc&r.zv)|(l.qr&d.we&r.qv);
    d.rz=(l.zv&d.xc&r.rz)|(l.rz&d.we&r.qr);
    return 0;
  }

  int build(int now,int left,int right)
  //建树
  {
    if(left==right)
      {
        val[now].qr=val[now].zv=val[now].we=val[now].xc=1;
        val[now].qz=val[now].rv=val[now].qv=val[now].rz=0;
        return 0;
      }
    int mid=(left+right)>>1;
    val[now].we=val[now].xc=0;
    build(now<<1,left,mid);
    build(now<<1|1,mid+1,right);
    merge(val[now],val[now<<1],val[now<<1|1]);
    return 0;
  }

  int rchange(int now,int left,int right,int pos,int cval)
  //将(1,pos)这个点与(2,pos)这个点的联通性变成cval
  {
    if(left==right)//说明当前这个位置就是pos,直接修改
      {
        val[now].qz=val[now].rv=val[now].qv=val[now].rz=cval;
        return 0;
      }
    int mid=(left+right)>>1;
    if(pos<=mid)//寻找pos
      {
        rchange(now<<1,left,mid,pos,cval);
      }
    else
      {
        rchange(now<<1|1,mid+1,right,pos,cval);
      }
    merge(val[now],val[now<<1],val[now<<1|1]);//更新这个点的val
    return 0;
  }

  int uchange(int now,int left,int right,int pos,int cval)
  //将(1,pos)这个点和(1,pos+1)这个点之间的联通性修改为cval
  {
    int mid=(left+right)>>1;
    if(mid==pos)//如果中点就是pos,直接修改w->e的值
      {
        val[now].we=cval;
        merge(val[now],val[now<<1],val[now<<1|1]);
        return 0;
      }
    if(pos<=mid)//寻找pos
      {
        uchange(now<<1,left,mid,pos,cval);
      }
    else
      {
        uchange(now<<1|1,mid+1,right,pos,cval);
      }
    merge(val[now],val[now<<1],val[now<<1|1]);
    return 0;
  }

  int dchange(int now,int left,int right,int pos,int cval)
  //将(2,pos)这个点和(2,pos+1)这个点的联通性修改为cval
  {
    int mid=(left+right)>>1;
    if(mid==pos)//如果中点就是pos,那么将x->c的值修改
      {
        val[now].xc=cval;
        merge(val[now],val[now<<1],val[now<<1|1]);
        return 0;
      }
    if(pos<=mid)//寻找pos
      {
        dchange(now<<1,left,mid,pos,cval);
      }
    else
      {
        dchange(now<<1|1,mid+1,right,pos,cval);
      }
    merge(val[now],val[now<<1],val[now<<1|1]);//更新这个点的val
    return 0;
  }

  data query(int now,int left,int right,int s,int t)
  //询问s到t区间的联通性(用一个data表示)
  {
    if((s<=left)&&(right<=t))
      {
        return val[now];
        //如果已经被完全包含,直接返回这个点的val
      }
    int mid=(left+right)>>1;
    if(s>mid)
      {
        return query(now<<1|1,mid+1,right,s,t);
        //如果完全包含于右区间,返回右区间的寻找结果
      }
    else if(t<=mid)
      {
        return query(now<<1,left,mid,s,t);
        //如果完全含于左区间,返回左区间的寻找结果
      }
    else
      {
        data res=val[now];
        merge(res,query(now<<1,left,mid,s,t),query(now<<1|1,mid+1,right,s,t));
        //两个区间一定是连起来的,直接用val[now]的信息合并左边和右边
        return res;
      }
  }
};

segment_tree st;
int n;
char ch[10];

inline int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

int main()
{
  n=read();
  st.build(1,1,n);//首先建树
  while(scanf("%s",ch)!=EOF)
    {
      if(ch[0]=='E')
        {
          break;
        }
      int ax=read(),ay=read(),bx=read(),by=read();
      if(ay>by)//保证a一定在b右侧
        {
          std::swap(ax,bx);
          std::swap(ay,by);
        }
      if(ch[0]=='O')
        {
      //分情况讨论Open的道路
          if(ay==by)
            {
              st.rchange(1,1,n,ay,1);
            }
          else if(ax==1)
            {
              st.uchange(1,1,n,ay,1);
            }
          else
            {
              st.dchange(1,1,n,ay,1);
            }
        }
      else if(ch[0]=='C')
        {
          //分情况讨论Close的道路
          if(ay==by)
            {
              st.rchange(1,1,n,ay,0);
            }
          else if(ax==1)
            {
              st.uchange(1,1,n,ay,0);
            }
          else
            {
              st.dchange(1,1,n,ay,0);
            }
        }
      else
        {
          data res=st.query(1,1,n,ay,by),l=st.query(1,1,n,1,ay),r=st.query(1,1,n,by,n);
          //res是[ay,by]的联通性,l是[1,ay]的联通性,r是[by,n]的联通性
          int ans;
          //下面是分情况讨论a和b的横坐标,计算过程就是上面的
          if((ax==1)&&(bx==1))
            {
              ans=res.qr|(l.rv&res.rz)|(r.qz&res.qv)|(l.rv&r.qz&res.zv);
            }
          else if((ax==1)&&(bx==2))
            {
              ans=res.qv|(l.rv&res.zv)|(r.qz&res.qr)|(l.rv&r.qz&res.rz);
            }
          else if((ax==2)&&(bx==1))
            {
              ans=res.rz|(l.rv&res.qr)|(r.qz&res.zv)|(l.rv&r.qz&res.qv);
            }
          else
            {
              ans=res.zv|(l.rv&res.qv)|(r.qz&res.rz)|(l.rv&r.qz&res.qr);
            }
          if(ans)
            {
              puts("Y");
            }
          else
            {
              puts("N");
            }
        }
    }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值