[SHOI2008]堵塞的交通traffic
Description
有一天,由於某種穿越現象作用,你來到了傳說中的小人國。小人國的布局非常奇特,整個國家的交通系統可以被看成是一個2行C列的矩形網格,網格上的每個點代表一個城市,相鄰的城市之間有一條道路,所以總共有2C個城市和3C-2條道路。 小人國的交通狀況非常槽糕。有的時候由於交通堵塞,兩座城市之間的道路會變得不連通,直到擁堵解決,道路才會恢復暢通。初來咋到的你決心毛遂自薦到交通部某份差事,部長聽說你來自一個科技高度發達的世界,喜出望外地要求你編寫一個查詢應答系統,以挽救已經病入膏肓的小人國交通系統。 小人國的交通部將提供一些交通信息給你,你的任務是根據當前的交通情況回答查詢的問題。交通信息可以分為以下幾種格式:
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
嗯,這題是一道線段樹神題。線段樹要維護一些信息,維護區間內左上左下,右上右下的連通情況,以及第一行和第二行是否可以向外延伸(共計$luru、lurd、luld、ldru、ldrd、rurd、road[0/1]$8條信息)。
那麽我們維護這些信息有什麽用呢?
維護這個信息是為了區間合並用的。我們先畫個圖(如上圖),我現在要將兩個區間合並。
新的信息如何維護?
\(lu1\)-->\(ru2:lu1\)-->\(ru1\)+第一行連通+\(lu2\)-->\(ru2\)
\(lu1\)-->\(ru2:lu1\)-->\(rd1\)+第二行連通+\(ld2\)-->\(ru2\)
上面我列舉了\(lu1\)-->\(ru2\)的情況,其他的7中情況都有類似的合並方式,我就不一一列舉了,對這代碼和圖就能理解。
至於如何判斷連通?同樣對著代碼和圖理解下即可。
(ps:附上一圖,以便check理解)
#include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define inf 0x7f7f7f7f using namespace std; typedef long long ll; typedef unsigned int ui; typedef unsigned long long ull; inline int read(){ int x=0,f=1;char ch=getchar(); for (;ch<‘0‘||ch>‘9‘;ch=getchar()) if (ch==‘-‘) f=-1; for (;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=(x<<1)+(x<<3)+ch-‘0‘; return x*f; } inline void print(int x){ if (x>=10) print(x/10); putchar(x%10+‘0‘); } const int N=1e5; int n; struct Segment{ #define ls (p<<1) #define rs (p<<1|1) struct AC{ bool luru,luld,lurd,ldru,rurd,ldrd;//6種連通方式 bool road[2]; void init(){luld=luru=ldru=rurd=0,luru=ldrd=1;} void add(bool flag){luld=rurd=lurd=ldru=flag;} }tree[N*4+10]; AC updata(AC x,AC y){ AC ans; ans.road[0]=y.road[0],ans.road[1]=y.road[1];//因為第一行和第二行的連通是互相的,所以找哪個都無所謂 ans.luru=ans.luld=ans.lurd=ans.ldru=ans.rurd=ans.ldrd=0; if ((x.luru&&x.road[0]&&y.luru)||(x.lurd&&x.road[1]&&y.ldru)) ans.luru=1; if ((x.ldrd&&x.road[1]&&y.ldrd)||(x.ldru&&x.road[0]&&y.lurd)) ans.ldrd=1; if ((x.luru&&x.road[0]&&y.lurd)||(x.lurd&&x.road[1]&&y.ldrd)) ans.lurd=1; if ((x.ldrd&&x.road[1]&&y.ldru)||(x.ldru&&x.road[0]&&y.luru)) ans.ldru=1; if ((x.luld)||(x.luru&&x.road[0]&&y.luld&&x.road[1]&&x.ldrd)) ans.luld=1; if ((x.rurd&&x.road[0]&&y.luru&&x.road[1]&&y.ldrd)||(y.rurd)) ans.rurd=1; //更新的話自己照著圖理解 return ans; } void build(int p,int l,int r){ if (l==r){ tree[p].init(); return; } int mid=(l+r)>>1; build(ls,l,mid),build(rs,mid+1,r); } void insert1(int p,int l,int r,int x,bool flag){ if (l==r){ tree[p].add(flag); return; } int mid=(l+r)>>1; if (x<=mid) insert1(ls,l,mid,x,flag); if (x>mid) insert1(rs,mid+1,r,x,flag); tree[p]=updata(tree[ls],tree[rs]); } void insert2(int p,int l,int r,int x,int y,bool flag){ if (l==r){ tree[p].road[y-1]=flag; return; } int mid=(l+r)>>1; if (x<=mid) insert2(ls,l,mid,x,y,flag); if (x>mid) insert2(rs,mid+1,r,x,y,flag); tree[p]=updata(tree[ls],tree[rs]); } AC query(int p,int l,int r,int x,int y){ if (x<=l&&r<=y) return tree[p]; int mid=(l+r)>>1; AC ans1,ans2; bool left=0,right=0; if (x<=mid) ans1=query(ls,l,mid,x,y),left=1; if (y>mid) ans2=query(rs,mid+1,r,x,y),right=1; if (left&&right) return updata(ans1,ans2); return left?ans1:ans2; } bool check(int r1,int c1,int r2,int c2){ AC pre,now,last; if (c1>c2) swap(c1,c2),swap(r1,r2); pre=query(1,1,n,1,c1); now=query(1,1,n,c1,c2); last=query(1,1,n,c2,n); //pre的右邊和now的左邊是重合的,now的右邊和las的左邊是重合的。不過pre記錄的是1~c1的聯通情況,和now不同,所以要分3個區間討論 if (r1==r2){//討論自己按著圖理解一下 if ((r1==1)&&((now.luru)||(pre.rurd&&now.ldru)||(now.lurd&&last.luld)||(pre.rurd&&now.ldrd&&last.luld))) return 1; if ((r1==2)&&((now.ldrd)||(pre.rurd&&now.lurd)||(now.ldru&&last.luld)||(pre.rurd&&now.luru&&last.luld))) return 1; }else{ if ((r1==1)&&((now.lurd)||(pre.rurd&&now.ldrd)||(now.luru&&last.luld)||(pre.rurd&&now.ldru&&last.luld))) return 1; if ((r1==2)&&((now.ldru)||(pre.rurd&&now.luru)||(now.ldrd&&last.luld)||(pre.rurd&&now.lurd&&last.luld))) return 1; } return 0; } }Tree; char s[10]; int main(){ n=read(); Tree.build(1,1,n); while (1){ scanf("%s",s); if (s[0]==‘E‘) break; int r1=read(),c1=read(),r2=read(),c2=read(); if (s[0]==‘O‘){ if (c1==c2) Tree.insert1(1,1,n,c1,1); else Tree.insert2(1,1,n,min(c1,c2),r1,1); } if (s[0]==‘C‘){ if (c1==c2) Tree.insert1(1,1,n,c1,0); else Tree.insert2(1,1,n,min(c1,c2),r1,0); } if (s[0]==‘A‘){ Tree.check(r1,c1,r2,c2)?putchar(‘Y‘):putchar(‘N‘); putchar(‘\n‘); } } return 0; }
[SHOI2008]堵塞的交通traffic