【BZOJ5288】[HNOI2018]游戲(拓撲排序)


【BZOJ5288】[HNOI2018]游戲(拓撲排序)

題面

BZOJ
洛谷

題解

去年省選的時候這題給我亂搞整過去整過去了,也是虐心了。。。。
所以當然是來講正兒八經的正確做法啦。

很明顯,我們需要預處理答案。設\(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\),反過來同理。
然后拓撲序確定拓展順序。
這樣子每個點被拓展的次數就是線性的啦QwQ

#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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM