zoukankan      html  css  js  c++  java
  • 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;
    }

      

    代码:

  • 相关阅读:
    LINQ语句中的.AsEnumerable() 和 .AsQueryable()的区别
    Linq扩展方法之All 、Any
    Linq扩展方法之Aggregate 对序列应用累加器函数
    C#开发的进化史
    C#选择文件、选择文件夹、打开文件(或者文件夹)
    XPath操作XML文档
    文档对象模型操作xml文档
    XML简介
    php获取某经纬度附近地点位置
    Yii中实例化类的四种方式
  • 原文地址:https://www.cnblogs.com/mjtcn/p/10424510.html
Copyright © 2011-2022 走看看