[BZOJ5288][拓扑序]HNOI2018:游戏

BZOJ5288

这题暴力 n 2 n^2 n2很好想,暴力扩展
而一个房间能到达的地方是可合并的,如果这个房间u能到达一个房间v,那么房间v能到达的所有地方u都可以到达
我们发现如果一个门的钥匙在它右边,那么左边就过不去
在它左边同理
那么就分上面两种情况给 i i i i + 1 i+1 i+1连一下边,拓扑排序就好了
时间复杂度 O ( n ) O(n) O(n)(不会证)

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1e6+5;
int head[N],nxt[N],vis[N],tot=0;
struct data{int x,y;}d[N];
int n,m,p,id,to[N];
int pos[N],l[N],r[N],in[N];
inline void add(int x,int y){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;++in[y];}
inline void init(){
    pos[n]=-1;id=1;
    for(int i=1;i<=n;++i){
	   to[i]=id;
	   if(pos[i]) l[id]=r[id]=id++;
    }
	--id;
    for(int i=1;i<=m;++i){
	   int x=to[d[i].x],y=to[d[i].y];
	   pos[x]=y;
	   if(x<y) add(x,x+1);
	   else add(x+1,x);
    }
}
inline int ok(int x,int y){
    if(y<1 || y>id) return 0;
    if(x<y) --y;
    return l[x]<=pos[y]&&pos[y]<=r[x];
}
queue<int>q;
void dfs(){
    for(int i=1;i<=id;++i)if(!in[i])q.push(i);
    while(!q.empty()){
	   int u=q.front();q.pop();
	   bool f=1;
	   while(f){
		  f=0;
		  while(ok(u,l[u]-1)) l[u]=l[l[u]-1],f=1;
		  while(ok(u,r[u]+1)) r[u]=r[r[u]+1],f=1;
	   }
	   for(int i=head[u];i;i=nxt[i]) if(!(--in[vis[i]])) q.push(vis[i]);
    }
}
int main(){
    n=read(),m=read(),p=read();
    for(int i=1;i<=m;++i){
	   d[i].x=read(),d[i].y=read();
	   pos[d[i].x]=d[i].y;
    }
    init();dfs();
    int x,y;
    while(p--){
	   x=to[read()],y=to[read()];
	   if(l[x]<=y && y<=r[x]) puts("YES");
	   else puts("NO");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值