• BZOJ5288 & 洛谷4436 & LOJ2508:[HNOI/AHOI2018]游戏——题解


    https://www.lydsy.com/JudgeOnline/problem.php?id=5288

    https://www.luogu.org/problemnew/show/P4436

    https://loj.ac/problem/2508

    UPD:重温了一下发现代码只能用C++(NOI)才能过了,不知道原因,但也懒得重写了,反正是对的。

    看洛谷题解里面清一色的暴力,连唯一正解也是用了奇技淫巧才过(当然本题解参考了那个题解)。

    于是难受的我来简单说一下“正解”(如有错误请指出orz)。

    先从暴力开始,对于每个点我们暴力找到其能够到达的最大的区间[l,r]。

    一个优化:我们在加入新的点进入这个区间的时候,可以把该点的答案区间合并进区间内。

    接下来是正解,首先是对于每个门i,如果钥匙在其左边则add(i+1,i),否则add(i,i+1),其中边的含义是从入点无法到达出点。

    于是对这个图拓扑排序后可发现,点u的答案区间一定不包含比其排名大的点,则在暴力优化的帮助下,我们可以证明出需要我们暴力更新的次数只有O(n)次。

    但是为什么又要按照那篇题解所说,“初始化序列倒着加会快”呢?

    我把数据下载下来才发现一个坑。

    对于不在拓扑图上的点,其更新后的答案区间可能会覆盖与它同级甚至比它排名小的点的答案区间,这样暴力优化就无用武之地,复杂度退化到O(n^2)。

    所以我们必须缩点,才能保证算法的复杂度。

    (话句话讲就是出题人都如此煞费苦心卡掉了复杂度不对的正解却让暴力AC了)

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=1e6+5;
    inline int read(){
        int X=0,w=0;char ch=0;
        while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
        while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
        return w?-X:X;
    }
    struct node{
        int to,nxt;
    }e[N];
    struct data{
        int x,y;
    }d[N];
    int n,m,p,head[N],cnt,id,to[N];
    int key[N],l[N],r[N],deg[N];
    inline void add(int u,int v){
        e[++cnt].to=v;e[cnt].nxt=head[u];head[u]=cnt;++deg[v];
    }
    inline bool pan(int x,int y){
        if(y<1||y>id)return 0;
        if(x<y)--y;
        return l[x]<=key[y]&&key[y]<=r[x];
    }
    queue<int>q;    
    void dfs(){
        for(int i=1;i<=id;++i)if(!deg[i])q.push(i);
        while(!q.empty()){
            int u=q.front();q.pop();
            bool flag=1;
            while(flag){
                flag=0;
                while(pan(u,l[u]-1))l[u]=l[l[u]-1],flag=1;
                while(pan(u,r[u]+1))r[u]=r[r[u]+1],flag=1;
            }
            for(int i=head[u];i;i=e[i].nxt){
                int v=e[i].to;
                if(!(--deg[v]))q.push(v);
            }
        }
    }
    inline void init(){
        key[n]=-1;id=1;
        for(int i=1;i<=n;++i){
            to[i]=id;
            if(key[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];
            key[x]=y;
            if(x<y)add(x,x+1);
            else add(x+1,x);
        }
    }
    int main(){
        n=read(),m=read(),p=read();
        for(int i=1;i<=m;++i){
            d[i].x=read(),d[i].y=read();
            key[d[i].x]=d[i].y;
        }
        init();dfs();
        for(int i=1;i<=p;++i){
            int x=to[read()],y=to[read()];
            if(l[x]<=y&&y<=r[x])putchar('Y'),putchar('E'),putchar('S'),putchar('
    ');
            else putchar('N'),putchar('O'),putchar('
    ');
        }
        return 0;
    }

    +++++++++++++++++++++++++++++++++++++++++++

     +本文作者:luyouqi233。               +

     +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

    +++++++++++++++++++++++++++++++++++++++++++

  • 相关阅读:
    Visual C# 2005中编写Socket网络程序
    [ASP.NET缓存BUG]这几天遇到的头痛问题之一,晚上遇到终于解决一劳永逸
    检测远程URL是否存在的三种方法<转>
    C#开源资源大汇总
    Asp.Net中动态页面转静态页面
    开发人员必进的网站
    基于反向代理的Web缓存加速——可缓存的CMS系统设计
    解决MVC3 服务器无法在已发送 HTTP 标头之后设置状态 问题
    HyperLink 控件控制图片宽度高度的几种方法
    C#进程注入
  • 原文地址:https://www.cnblogs.com/luyouqi233/p/9127342.html
Copyright © 2020-2023  润新知