[HNOI/AHOI2018]游戏


题解

一个点的影响区间显然是一段连续的区间
这样一个显然的\(O(n^2)\)暴力就是我们可以将一个点向左右扩展,处理出从这个点出发能到达的左右端点\(lp,rp\)
然后考虑怎么优化这个暴力
首先如果我们从点\(u\)向左扩展到\(l\),并且这个\(l\)已经被扩展过
那么我们就可以直接让\(lp[u]\)变成\(lp[l]\)
所以我们如果能确定一个顺序使得更新到这个点的时候所有ta能更新的点已经被更新过的话这个复杂度就变得肥肠优秀了
我们考虑每个门和钥匙\((x,Key_x)\)
如果钥匙在门的左边,那么显然门右边的点都过不了这个门
那么我们就对这个门\(x\)连一条\((x+1,x)\)
表示反正右边的点过不了门,所以先处理门右边的部分,再处理门左边的部分
反之就连\((x,x+1)\)
然后拓扑排序的时候当处理到这个点的时候ta能更新到的点都已经被更新了
所以直接暴力更新就可以了
然后注意如果两个屋子之间没有门就把他们缩起来
复杂度\(O(n+m)\)

代码

/*
Key[i] <= i 钥匙在左门在右
  add_edge(i+1,i)
Key[i] > i 钥匙在右门在左
  add_edge(i,i+1)

*/
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 1000005 ;
using namespace std ;

inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}

bool exist[M] ;
int n , m , cnt , num ;
int f[M] , idx[M] , Key[M] ;
int d[M] , hea[M] , xp[M] , yp[M] , lp[M] , rp[M] ;
struct E {
	int nxt , to ;
} edge[M] ;
int find(int x) {
	if(f[x] != x)
		f[x] = find(f[x]) ;
	return f[x] ;
}
inline void add_edge(int from , int to) {
	edge[++num].nxt = hea[from] ;
	edge[num].to = to ;
	hea[from] = num ;
}

inline void topsort() {
	queue < int > q ;
	for(int i = 1 ; i <= cnt ; i ++) {
		lp[i] = rp[i] = i ;
 		if(!d[i])
			q.push(i) ;
	}
	while(!q.empty()) {
		int u = q.front() ; q.pop() ;
		bool suc = true ;
		while(suc) {
			suc = false ;
			if(lp[u] > 1 && Key[lp[u] - 1] >= lp[u] && Key[lp[u] - 1] <= rp[u])
				lp[u] = lp[lp[u] - 1] , suc = true ;
			if(rp[u] < cnt && Key[rp[u]] <= rp[u] && Key[rp[u]] >= lp[u])
				rp[u] = rp[rp[u] + 1] , suc = true ;
		}
		for(int i = hea[u] ; i ; i = edge[i].nxt) {
			int v = edge[i].to ;
			-- d[v] ;
			if(!d[v])
				q.push(v) ;
		}
	}
}
int main() {
	n = read() ; m = read() ; int Case = read() ;
	for(int i = 1 ; i <= n ; i ++)
		f[i] = i ;
	for(int i = 1 ; i <= m ; i ++) {
		xp[i] = read() ; yp[i] = read() ;
		exist[xp[i]] = true ;
	}
	for(int i = 1 ; i < n ; i ++) {
		if(exist[i]) continue ;
		int x = find(i) , y = find(i + 1) ;
		if(x != y) f[y] = x ;
	}
	for(int i = 1 ; i <= n ; i ++) {
		if(find(i) != find(i - 1)) ++ cnt ;
		idx[i] = cnt ;
	}
	for(int i = 1 ; i <= m ; i ++)
		Key[idx[xp[i]]] = idx[yp[i]] ;
	for(int i = 1 ; i < cnt ; i ++) {
		if(Key[i] <= i) {
			add_edge(i + 1 , i) ;
			++ d[i] ;
		}
		else {
			add_edge(i , i + 1) ;
			++ d[i + 1] ;
		}
	}
	topsort() ;
	int x , y ;
	while(Case --) {
		x = read() ; y = read() ;
		if(idx[y] >= lp[idx[x]] && idx[y] <= rp[idx[x]])
			printf("YES\n") ;
		else printf("NO\n") ;
	}
	return 0 ;
}
posted @ 2019-04-24 10:05  beretty  阅读(204)  评论(0编辑  收藏  举报