LOJ #2508. 「AHOI / HNOI2018」游戏 拓扑排序

好神仙的一道题!   

由于每个门对应的钥匙仅有一把,所以我们可以确定门和门之间的相对解锁顺序.   

比如,解锁门 $(x,x+1)$ 的要是在 $[1,x]$ 之间的话 $x+1$ 无论如何也到不了 $x$ 这一侧,但是 $x$ 有可能可以到达 $x+1$ 这边. 

所以我们就先去解锁 $x+1$,然后再去解锁 $x$,这样的话 $x$ 就可以直接继承 $x+1$ 的拓展结果了.      

code:   

#include <bits/stdc++.h>   
#define ll long long 
#define N 1000200 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;   
int edges=1; 
int hd[N],to[N<<1],nex[N<<1],deg[N];     
void add(int u,int v) 
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v;       
    deg[v]++;  
}
int n,m,Q,p[N],tot,key[N]; 
queue<int>q;  
void topo() 
{
    int i,j;  
    for(i=n;i;--i)  if(!deg[i])  q.push(i);    
    while(!q.empty()) 
    {
        int u=q.front(); q.pop();   p[++tot]=u;    
        for(int i=hd[u];i;i=nex[i])   
        {
            int v=to[i];   
            --deg[v];  
            if(!deg[v])   
                q.push(v);   
        }
    }
} 
int L[N],R[N];    
void cal(int x) 
{
    int l=x,r=x;   
    while(1) 
    {
        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(l==pl&&r==pr)   
            break;     
    }
    L[x]=l,R[x]=r;   
}
int main() 
{ 
    //  setIO("input");  
    int i,j;  
    scanf("%d%d%d",&n,&m,&Q);   
    for(i=1;i<=m;++i) 
    {
        int x,y;  
        scanf("%d%d",&x,&y);  
        key[x]=y;   
        if(y<=x)   
            add(x+1,x);   
        else 
            add(x,x+1);  
    }
    topo();      
    for(i=1;i<=n;++i) 
        L[i]=R[i]=i;   
    for(i=1;i<=n;++i)   
        cal(p[i]);   
    while(Q--) 
    {
        int s,t; 
        scanf("%d%d",&s,&t);    
        if(L[s]<=t&&t<=R[s])   
            printf("YES\n");  
        else 
            printf("NO\n");  
    }
    return 0; 
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值