「HNOI2018」游戏

「HNOI2018」游戏

解题思路

首先没有锁上的门可以缩点缩掉,然后对于一扇锁上的门,如果钥匙在左边,那么右边就永远不可能到达左边,同理如果钥匙在右边,左边就永远不可能到达右边。

然后考虑一个暴力的做法,对于一个点不断尝试向左向右扩展,直到不能扩展位置得到其最终的区间,这个过程可以记忆化一下每个已经算过的点的区间,直接做最坏还是 \(\mathcal O(n^2)\)然而随机化可过

对于一扇门,如果钥匙在左侧,就让门右侧的点向门左侧的点连一条边,这样可以得到一个 \(\text{DAG}\) ,因为右边到达不了左边,就先让右侧先扩展左侧利用右侧的答案,这样每一个区间只会向右被扩展一次向左被扩展一次,之后这个区间就被合并掉了,区间合并次数是 \(\mathcal O(m)\) 的,所以总复杂度 \(\mathcal O(n+m)\)然而乱搞碾标算

code
/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 2000005;
queue<int> Q;
vector<int> g[N];
int bel[N], lock[N], l[N], r[N], deg[N], L[N], R[N], n, m, q, cnt;
int main(){
    read(n), read(m), read(q);
    for(int i = 1, x, y; i <= m; i++) 
        read(x), read(y), lock[x] = y;
    for(int i = 1; i <= n; i++) if(!bel[i]){
        bel[i] = ++cnt; int j = i;
        for(; j < n && !lock[j]; j++) bel[j+1] = bel[i];
        L[cnt] = i, R[cnt] = j, l[cnt] = r[cnt] = cnt;
    }
    for(int i = 1; i <= n; i++)
        if(lock[i] && lock[i] <= i) 
            g[bel[i+1]].push_back(bel[i]), deg[bel[i]]++;
    for(int i = 1; i <= cnt; i++) if(!deg[i]) Q.push(i);
    for(; !Q.empty(); Q.pop()){
        int u = Q.front(), lstL, lstR;
        do{
            lstL = l[u], lstR = r[u];   
            if(l[u] > 1 && lock[L[l[u]]-1] >= L[l[u]] && lock[L[l[u]]-1] <= R[r[u]]) l[u] = l[l[u]-1];
            if(r[u] < cnt && lock[R[r[u]]] >= L[l[u]] && lock[R[r[u]]] <= R[r[u]]) r[u] = r[r[u]+1];
        }while(l[u] != lstL || r[u] != lstR);
        for(int i = 0; i < (int) g[u].size(); i++) 
            if(!--deg[g[u][i]]) Q.push(g[u][i]);
    }
    while(q--){
        int x, y; read(x), read(y);
        if(bel[y] >= l[bel[x]] && bel[y] <= r[bel[x]]) puts("YES"); else puts("NO");
    }
    return 0;
}   

转载于:https://www.cnblogs.com/mangoyang/p/10514771.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值