去年省選的時候這題給我亂搞整過去整過去了,也是虐心了。。。。
因此固然是來說正兒八經的正確作法啦。ios
很明顯,咱們須要預處理答案。設\(L[i],R[i]\)表示從\(i\)出發可以到達的區間範圍。
如今咱們要作的就是預處理這個\(L[i],R[i]\)。
首先考慮一個點如何向外暴力拓展,若是它在拓展過程當中碰到了一個已經被拓展過的節點,那麼顯然也能夠到達那個點能夠到達的全部位置,而後直接並一下就好啦。
而後如今咱們要肯定拓展的順序。
對於一個門\((x,x+1)\)而言,若是鑰匙在\([1,x]\)這段內,顯然只有在\([1,x]\)這一側才能拓展到\([x+1,n]\),因此必定先拓展\(x+1\)再拓展\(x\),因此連邊\(x+1\rightarrow x\),反過來同理。
而後拓撲序肯定拓展順序。
這樣子每一個點被拓展的次數就是線性的啦QwQspa
#include<iostream> #include<cstdio> #include<queue> using namespace std; #define MAX 1000100 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Line{int v,next;}e[MAX]; int h[MAX],cnt=1,dg[MAX]; inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;dg[v]+=1;} int n,m,Q,p[MAX],tot,key[MAX]; void Topsort() { queue<int> Q; for(int i=n;i;--i)if(!dg[i])Q.push(i); while(!Q.empty()) { int u=Q.front();Q.pop();p[++tot]=u; for(int i=h[u];i;i=e[i].next) if(!--dg[e[i].v])Q.push(e[i].v); } } int L[MAX],R[MAX]; void Calc(int x) { int l=x,r=x; while(233) { int pl=l,pr=r; while(l>1&&(!key[l-1]||(l<=key[l-1]&&key[l-1]<=r)))l=L[l-1]; while(r<n&&(!key[r]||(l<=key[r]&&key[r]<=r)))r=R[r+1]; if(pl==l&&pr==r)break; } L[x]=l;R[x]=r; } int main() { n=read();m=read();Q=read(); for(int i=1;i<=m;++i) { int x=read(),y=read();key[x]=y; if(y<=x)Add(x+1,x); else Add(x,x+1); } Topsort(); for(int i=1;i<=n;++i)L[i]=R[i]=i; for(int i=1;i<=n;++i)Calc(p[i]); while(Q--) { int S=read(),T=read(); if(L[S]<=T&&T<=R[S])puts("YES"); else puts("NO"); } return 0; }