题意
有(n)个房间,第(i)个房间与第(i+1)个房间之间有一扇门,有些门上了锁。已知所有上了锁的门的编号,以及每一把锁在哪个房间,有多组询问:能否从(a)房间到达(b)房间?
思路
考虑优化暴力。
对于每一个节点,维护L[i],R[i],表示该节点能够到达最左右极点。
对于任意一扇上了锁的门,如果钥匙在门的左边,那么这扇门右边的房间肯定都到不了左边的房间,而左边的房间有可能达到右边的房间。如果钥匙在门的右边则同理。
也就是说,对于一扇上了锁的门,右边的每一个节点的[L,R]都不可能包含左边的节点的[L,R],而反过来是有可能的。
可以考虑这样的一种拓展顺序:
对于拓展序列中的每一个节点,位于它前面的任意节点都不可能包含它的区间。
显然这样的拓展顺序是最优的,因为我们的更新始终不会被干扰。
为了得到这样的拓展顺序,考虑采用拓扑排序。对于钥匙在门左侧的情况,从右边连向左边的节点,反之亦然。然后跑一遍拓扑排序即可得到我们需要的拓展顺序,最后暴力拓展即可。
但是这样写还是会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();
}