遊戲「AHOI / HNOI2018」

題意

有$n$個房間,第$i$個房間與第$i+1$個房間之間有一扇門,有些門上了鎖。已知全部上了鎖的門的編號,以及每一把鎖在哪一個房間,有多組詢問:可否從$a$房間到達$b$房間?node

思路

考慮優化暴力。c++

對於每個節點,維護L[i],R[i],表示該節點可以到達最左右極點。優化

對於任意一扇上了鎖的門,若是鑰匙在門的左邊,那麼這扇門右邊的房間確定都到不了左邊的房間,而左邊的房間有可能達到右邊的房間。若是鑰匙在門的右邊則同理。spa

也就是說,對於一扇上了鎖的門,右邊的每個節點的[L,R]都不可能包含左邊的節點的[L,R],而反過來是有可能的。code

能夠考慮這樣的一種拓展順序:排序

對於拓展序列中的每個節點,位於它前面的任意節點都不可能包含它的區間。get

顯然這樣的拓展順序是最優的,由於咱們的更新始終不會被幹擾。it

爲了獲得這樣的拓展順序,考慮採用拓撲排序。對於鑰匙在門左側的狀況,從右邊連向左邊的節點,反之亦然。而後跑一遍拓撲排序便可獲得咱們須要的拓展順序,最後暴力拓展便可。class

可是這樣寫仍是會T,因此須要進一步優化。程序

考慮這樣一種狀況:只有一個門上了鎖,而其餘全部門都沒有上鎖。

對於這樣的狀況,咱們的程序仍然有n的計算量,而事實上只須要1的計算量。

能夠對數據進行預處理,將不要鑰匙就能夠直接到達的點鎖在一塊兒,而後對縮完點的序列再進行拓撲排序,這樣對於上述狀況就能夠作的高效處理了。


代碼

無縮點環節,70分代碼

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	template<typename T> inline void read (T &x) {
		x=0;T f=1;char c=getchar();
		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}
	template<typename T> inline void write (T x) {
		if (x<0) putchar('-'),x=-x;
		if (x>=10) write(x/10);
		putchar((x%10)+'0');
	}

}

using namespace StandardIO;

namespace Solve {
	
	const int N=1000010;
	
	int n,m,p;
	int key[N],l[N],r[N],indeg[N];
	int cnt;
	int head[N];
	struct node {
		int to,next;
	} edge[N<<1];
	int num;
	int seq[N];
	
	inline void add (int a,int b) {
		edge[++cnt].to=b,edge[cnt].next=head[a],head[a]=cnt,++indeg[b];
	}
	inline void topsort () {
		queue<int> q;
		for (register int i=1; i<=n; ++i) if (!indeg[i]) q.push(i);
		while (!q.empty()) {
			int cur=q.front();q.pop(),seq[++num]=cur;
			l[cur]=r[cur]=cur;
			for (register int i=head[cur]; i; i=edge[i].next) {
				int to=edge[i].to;
				--indeg[to];
				if (!indeg[to]) q.push(to);
			}
		}
	}

	inline void Main () {
		read(n),read(m),read(p);
		for (register int i=1; i<=m; ++i) {
			int x,y;
			read(x),read(y);
			key[x]=y;
			if (y>x) add(x,x+1);
			else add(x+1,x);
		} 
		topsort();
		int counter=0;
		for (register int i=1; i<=n; ++i) {
			int now=seq[i];
			while (1) {
				int tl=l[now],tr=r[now];
				while (tl>1&&(!key[tl-1]||(tl<=key[tl-1]&&key[tl-1]<=tr))) tl=l[tl-1];
				while (tr<n&&(!key[tr]||(tl<=key[tr]&&key[tr]<=tr))) tr=r[tr+1];
				if (tl==l[now]&&tr==r[now]) break;
				l[now]=tl,r[now]=tr;
			}
		}
		for (register int i=1; i<=p; ++i) {
			int s,t;
			read(s),read(t);
			if (l[s]<=t&&t<=r[s]) puts("YES");
			else puts("NO");
		}
	}

}

int main () {
	Solve::Main();
}
相關文章
相關標籤/搜索