[SHOI2008]堵塞的交通traffic

题意

\(2*n\)个城市排成\(2\)\(n\)列的网格,相邻两个城市间有一条道路,初始时所有道路断开,支持以下操作:断开一条道路、连通一条道路、询问两个城市是否连通

思路

开始还以为很简单,结果是道线段树神仙题

将两行弄到一起,用一颗线段树来维护列

设当前区间为\([l,r]\)\(l1\)为左上,\(l2\)为左下,\(r1\)为右上,\(r2\)为右下,维护六个值(是否连通):

  1. \(L : (l1,l2)\),左上左下
  2. \(R : (r1,r2)\),右上右下
  3. \(U : (l1,r1)\),左上右上
  4. \(D : (l2,r2)\),左下右下
  5. \(P : (l1,r2)\),左上右下
  6. \(Q : (l2,r1)\),左下右上

pushup

全场最重要的操作,主要思路即通过两边求出的连通性以及两块中间的两条边推出整块的连通性(如果自己画图手推就会很好理解)

设上面的边为\(c1\),下面的边为\(c2\)

设左子树为\(a\),右子树为\(b\)

  1. \(L\) : 既可以从左边直接转移\((a.L)\),也可以先从上面的边跑到右边再从下面的边跑回去\((a.U->c1->b.L->c2->a.D)\)

  2. \(R\) : 类似\(L\)

  3. \(U\) : 可以从左边走\(c1\)到右边\((a.U->c1->b.U)\),也可以从左边走\(c2\)到右边\((a.P->c2->b.Q)\)

  4. \(D\) : 类似\(U\)

  5. \(P\) : 类似\(U\),选择\(c1(a.U->c1->b.P)\)或者\(c2(a.P->c2->b.D)\)

  6. \(Q\) : 类似\(P\)

modify

不同于我看过的几篇博客,对于修改操作可以直接在存道路的数组里面改,修改的这条道路会影响包含这个点的区间,所以修改之后调用\(modify\)函数递归到这个点重新进行\(pushup\)操作即可

query

询问操作即提取区间,这个操作做过几道线段树题目的都会吧

但是这里有一个问题,实际上从一个城市到另一个城市并不是一定走这个提取出的区间里面的道路,这意味着这条路径可能从区间外面绕一转之后才回来,所以我们还要提取两边的区间,用类似上面\(pushup\)的方法合并道路

Code

#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n;
char c[10];
int h[2][N],w[N];//横两排,竖排

struct Node
{
	int l,r;
	bool L,R;//左上左下,右上右下
	bool U,D;//左上右上,左下右下
	bool P,Q;//左上右下,右上左下 
}t[N<<2];


template <class T>
void read(T &x)
{
	char c;int sign=1;
	while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
	while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}

inline Node pushup(Node a,Node b)
{
	int u=h[0][a.r],d=h[1][a.r];
	Node ret;
	ret.l=a.l; ret.r=b.r;
	ret.L=(a.L) | (u&d&b.L&a.U&a.D);
	ret.R=(b.R) | (u&d&a.R&b.U&b.D);
	ret.U=(a.U&u&b.U) | (a.P&d&b.Q);
	ret.D=(a.D&d&b.D) | (a.Q&u&b.P);
	ret.P=(a.P&d&b.D) | (a.U&u&b.P);
	ret.Q=(a.Q&u&b.U) | (a.D&d&b.Q);
	return ret;
}
void build(int rt,int l,int r)
{
	if(l==r)
	{
		t[rt].l=t[rt].r=l;
		t[rt].U=t[rt].D=1;
		return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	t[rt]=pushup(t[rt<<1],t[rt<<1|1]);
}
void modify(int rt,int x)
{
	if(t[rt].l==t[rt].r)
	{
		t[rt].L=t[rt].R=w[t[rt].l];
		t[rt].P=t[rt].Q=w[t[rt].l];
		t[rt].U=t[rt].D=1;
		return;
	}
	int mid=(t[rt].l+t[rt].r)>>1;
	if(x<=mid) modify(rt<<1,x);
	else modify(rt<<1|1,x);
	t[rt]=pushup(t[rt<<1],t[rt<<1|1]);
}
Node query(int rt,int x,int y)
{
	if(x<=t[rt].l&&t[rt].r<=y) return t[rt];
	int mid=(t[rt].l+t[rt].r)>>1;
	if(x<=mid&&y<=mid) return query(rt<<1,x,y);
	if(y>mid&&x>mid) return query(rt<<1|1,x,y);
	Node ll=query(rt<<1,x,y),rr=query(rt<<1|1,x,y);
	return pushup(ll,rr);
}
void BOOL(bool x)
{
	if(x) printf("Y\n");
	else printf("N\n");
}
int main()
{
	read(n);
	build(1,1,n);
	while(1)
	{
		scanf("%s",c);
		if(c[0]=='E') break;
		int r1,c1,r2,c2;
		read(r1);read(c1);
		read(r2);read(c2);
		if(c1>c2) swap(c1,c2),swap(r1,r2);
		if(c[0]=='C')//断路 
		{
			if(r1==r2)
			{
				h[r1-1][c1]=0;
				modify(1,c1); modify(1,c2);
			}
			else
			{
				w[c1]=0;
				modify(1,c1);
			}
		}
		if(c[0]=='O')//通路 
		{
			if(r1==r2)
			{
				h[r1-1][c1]=1;
				modify(1,c1); modify(1,c2);
                //更新两头
			}
			else
			{
				w[c1]=1;
				modify(1,c1);
			}
		}
		if(c[0]=='A')//询问 
		{
			Node q=query(1,c1,c2),ll=query(1,1,c1),rr=query(1,c2,n);
			if(r1==1&&r2==1) BOOL((q.U)|(ll.R&q.Q)|(ll.R&q.D&rr.L));
			if(r1==1&&r2==2) BOOL((q.P)|(ll.R&q.D)|(q.U&rr.L));
			if(r1==2&&r2==1) BOOL((q.Q)|(ll.R&q.U)|(q.D&rr.L));
			if(r1==2&&r2==2) BOOL((q.D)|(ll.R&q.P)|(ll.R&q.U&rr.L));
		}
	}
	return 0;
}
posted @ 2019-08-30 19:08  擅长平地摔的艾拉酱  阅读(227)  评论(0编辑  收藏  举报
/*取消选中*/