! AHOI/HNOI2018游戏



啥题都要看题解,我咋这么菜!!!……

SOL:

我们需要预处理出两个数组\(l[i],r[i],i\)点到左/右最远能到达那个点,这样便于\(O(1)\)回答询问

当然可以左右枚举,但是为了节省时间,我们想要继承左右点可到达的信息

于是枚举顺序就十分重要了

\((x,x+1)\),若钥匙在\([1,x]\),只有在\([1,x]\)才能拓展到\([x+1,n]\),所以先拓展\(x+1\)再拓展\(x\),连边\(x+1→x\);反之同理

用拓扑排序处理顺序,然后枚举可到达即可

最开始加度数为0的点时从大到小,因为我们拓展时是先左端点再右端点,这样更快 我不会告诉你反过来会超时

时间复杂度线性

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f==1?x:-x;
}
const int N=1e6+4;
int n,m,Q,key[N],l[N],r[N],deg[N];
vector<int>e[N];
inline void calc(int x){
	static int pl,pr;
	pl=pr=x;
	while(1){
		while(pl>1&&(!key[pl-1]||(pl<=key[pl-1]&&key[pl-1]<=pr)))pl=l[pl-1];
		while(pr<n&&(!key[pr]||(pl<=key[pr]&&key[pr]<=pr)))pr=r[pr+1];
		if(pl==l[x]&&pr==r[x])return;
		l[x]=pl;r[x]=pr;
	}
}
inline void topsort(){
	queue<int>q;
	for(int i=n;i;i--)if(!deg[i])q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();
		calc(x);
		for(auto v:e[x])
			if(!(--deg[v]))q.push(v);
	}
}
int main(){
	n=read();m=read();Q=read();
	for(int i=1;i<=n;i++)l[i]=r[i]=i;
	for(int i=1,x,y;i<=m;i++){
		x=read();key[x]=y=read();
		if(y<=x){e[x+1].push_back(x);deg[x]++;}
		else{e[x].push_back(x+1);deg[x+1]++;}
	}
	topsort();
	while(Q--){
		static int x,y;
		x=read();y=read();
		if(l[x]<=y&&y<=r[x])puts("YES");
		else puts("NO");
	}
	return (0-0);
}

猜你喜欢

转载自www.cnblogs.com/aurora2004/p/12581410.html