5288: [Hnoi2018]游戏

5288: [Hnoi2018]游戏

链接

分析:

  考虑y<=x的怎么做,那么只能从左边走到右边。我们可以从最右边的点开始,一次确定每个点往右边可以走多少。

  L[x],R[x]分别是x向左向右最远走到的位置,初始L[x]=x,R[x]=x。R[n]=n,然后看n-1,如果n-1存在打开n-1这扇门的钥匙,那么说明n-1可以到n,相应的R[n-1]=R[n]。同样的考虑i,如果i可以打开第i扇门,那么R[i]=R[i+1],继续判断如果i可以打开第R[i]扇门,那么R[i]=R[R[i]]……

  于是这样可以求出每个往右延伸的范围。复杂度的证明:每扇门只会被打开一次,每个点只会被求扫一次,于是复杂度是$O(n)$

  如果y不小于x,考虑怎么做。将两个点之间的边确定方向(如果钥匙在左边,那么边是左->右,否则是右->左),然后对于找到一个点x,它既能向左走也可以向右走,从这个点开始,往左走到的是y,那么x->y这一段和y<=x的性质一样,做法也一样,扫一遍即可,往右走同理。最后处理x,直接暴力处理即可。

  复杂度:每个点只会存在于一条链中,暴力处理的点总复杂度是O(n)的。

  如果一条边上没有门呢?没法定向了,缩点即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}

const int N = 2000005;
int a[N], b[N];
int n, m, Q;

namespace BF1{
    int L[2005], R[2005]; bool vis[2005]; vector<int> vec[2005], d;
    void Mark(int x) {
        for (int j = 0; j < (int)vec[x].size(); ++j) vis[vec[x][j]] = 1;
    }
    void Calc(int x) {
        L[x] = R[x] = x;
        for (int i = 0; i <= n; ++i) vis[i] = 0;
        for (int i = 0; i < (int)d.size(); ++i) 
        vis[d[i]] = 1;
        Mark(x);
        while (1) {
            if (!vis[R[x]] && !vis[L[x] - 1]) break;
            if (vis[R[x]]) {
                R[x] ++; Mark(R[x]);
            }
            if (vis[L[x] - 1]) {
                L[x] --; Mark(L[x]);
            }
        }
    }
    void Main() {
        for (int i = 1; i <= m; ++i) vec[b[i]].push_back(a[i]);
        for (int i = 1; i <= m; ++i) vis[a[i]] = 1;
        for (int i = 1; i < n; ++i) if (!vis[i]) d.push_back(i);
        for (int i = 1; i <= n; ++i) Calc(i);
        while (Q--) {
            int s = read(), t = read();
            if (L[s] <= t && t <= R[s]) puts("YES");
            else puts("NO");
        }
    }
}

int head[N], fa[N], vis[N], chu[N], nxt[N], pre[N], L[N], R[N], pos[N], En;
vector<int> d;
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
inline void Union(int x,int y) { if (x != y) fa[y] = x;}
inline void add_edge(int x,int y) { x = find(x), y = find(y); chu[x] ++; }

void Calc(int x) {
    int z = x;
    while (z >= 1 && chu[z] != 0) z = pre[z]; if (z < 1) z = nxt[z];
    for (int i = z; i <= x; i = nxt[i]) { 
        L[i] = i;R[i] = i;
        int now = pre[L[i]];
        if (now < z) continue; 
        while (L[i] <= pos[now] && pos[now] <= R[i]) { 
            L[i] = L[now], now = pre[L[i]]; 
        }
    }
    z = x;
    while (z <= n && chu[z] != 0) z = nxt[z]; if (z > n) z = pre[z]; 
    for (int i = z; i >= x; i = pre[i]) {  
        L[i] = i;R[i] = i;
        int now = R[i]; 
        if (nxt[now] > z) continue;
        while (L[i] <= pos[now] && pos[now] <= R[i]) { 
            R[i] = R[nxt[now]], now = R[i]; 
        }
    }
    while (1) { 
        bool f = 0;
        if (L[x] <= pos[R[x]] && pos[R[x]] <= R[x]) R[x] = nxt[R[x]], f = 1; 
        if (L[x] <= pos[pre[L[x]]] && pos[pre[L[x]]] <= R[x]) L[x] = pre[L[x]], f = 1;
        if (!f) break; 
    }
}
void solve() {
    for (int i = 1; i <= m; ++i) vis[a[i]] = 1;
    for (int i = 1; i <= n; ++i) fa[i] = i;
    for (int i = 1; i < n; ++i) if (!vis[i]) Union(i, i + 1); 
    int last = 0;
    for (int i = 1; i <= n; ++i) 
        if (find(i) == i) pre[i] = last, nxt[last] = i, last = i;
    nxt[last] = n + 1; pre[n + 1] = last;
    
    for (int i = 1; i <= m; ++i) {
        a[i] = find(a[i]), b[i] = find(b[i]); 
        if (b[i] <= a[i]) add_edge(a[i], nxt[a[i]]); 
        else add_edge(nxt[a[i]], a[i]);
        pos[a[i]] = b[i]; 
    }
    for (int i = 1; i <= n; i = nxt[i]) { 
        if (chu[i] == 2 || (i == nxt[0] && chu[i] == 1) || (i == pre[n + 1] && chu[i] == 1)) 
        Calc(i);
    }
}
int main() {
    n = read(), m = read(), Q = read();    
    for (int i = 1; i <= m; ++i) 
        a[i] = read(), b[i] = read();
    if (n <= 1000 && m <= 1000) { BF1::Main(); return 0; }
    if (m == 0) { while (Q --) puts("YES"); return 0; }
    solve();
    for (int i = 1; i <= n; ++i) if (!L[i]) L[i] = L[find(i)];
    for (int i = n; i >= 1; --i) if (R[i]) R[i] = nxt[R[i]] - 1;
    for (int i = 1; i <= n; ++i) if (!R[i]) R[i] = R[find(i)];
    
    while (Q--) {
        int s = read(), t = read();
        if (L[s] <= t && t <= R[s]) puts("YES");
        else puts("NO");
    }
    return 0;
}

 

 

  

代码:

 

posted @ 2019-02-23 22:07  MJT12044  阅读(219)  评论(0编辑  收藏  举报