• NOI2018 Day1 归程(return)


    第一次参加NOI,当然,我没去现场做,只是在网络同步赛做了而已。
    那网站,特别特别卡啊……
    最后只交了第一题,原本认为能AC,但是因为某些原因只有50分。
    我这可怜的第一次啊……

    题目

    题目点此处下载,密码:ghn5

    思考过程

    看完了所有题,就来思考第一题。
    曾经的GDKOI和GDOI给我留下了不可磨灭的印象:感觉上这些题似乎都只能靠水分。
    所以,我从一开始就想着打水法。
    想着想着,感觉可以打个80分。好开心!
    开打。
    打了4000多byte,完成得差不多了,突然发现,啊,我想到正解了!!!
    把这个4000多byte的程序弃掉,思考了一阵子,然后开打。
    打了好久,有3000多byte,尴尬的是,过了前四个样例,却,被第五个样例卡掉了。
    我好慌,好慌……
    然后发现,我程序跑得慢的原因,是最短路花的时间太多了。
    一世英名,卡在了最短路上。
    我看看那个TLE掉的Dijsktra,不信邪,打了个SPFA。
    依然被卡,于是,我决定重打一遍Dijsktra!
    这一次,秒过……
    然后我就愉快地交了程序……

    解法

    题目的意思是在图中,先走一段没有积水的路径,这些代价是免费的,然后在走到终点。
    先想想该怎么搞离线的做法。
    首先,肯定要求一遍最短路。
    把问题转化一下,现在有积水的边不能走,你只能走没有积水的边。
    答案即是你可以走到的的点中,最短路值最小的。
    立体化地想一想,如果一开始没有水,然后雨越下越大,水位逐渐升高。
    原本图是一个联通块,然后随着一些边的淹没,被逐渐分成了许多小的联通块。
    不妨反过来想想。
    可以做一遍最大生成树,维护联通块中最短路的最小值,在做的过程中统计答案。
    这就是离线做法。
    怎么在线呢?
    维护联通块中最大值中,我用的是并查集。
    既然非要在线,那就用可持久化并查集!!!
    那样就可以询问历史版本,就可以在线做了,哈哈哈哈哈哈哈哈哈……
    如果点x所表示的集合要并到y所表示的集合中时,在x上记录一下两者联通的最高海拔。
    并试着更新一下y上维护的,表示联通块中最短路的最小值。
    然而,我们需要记录一下历史版本。怎么记录?
    直接暴力记录啊!反正最大生成树中,一共会连N1条边,那么,顶多更新N1次。
    在每个点上存它的历史状态,直接简单粗暴的在一个数组里记录一条信息,表示某个点,在某个水位时,最短路最小值是多少。
    搞完最大生成树后,排个序,然后一段区间对应的就是一个点的修改信息。
    那么在询问时,先在并查集上不断往上跳,直到跳不了(再跳就被水淹没了)。
    然后在这个点的修改信息中,二分出来,得出答案。
    时间复杂度:
    O(NlgNDijsktra+(MlgM+MlgN)+NlgN+QlgN)=O(MlgM+(N+M+Q)lgN)

    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define MAXN 200000
    #define MAXM 400000
    int n,m;
    struct edge
    {
        int u,v,l,a;
    } ed[MAXM+1];
    bool cmped(const edge &x,const edge &y)
    {
        return x.a>y.a;
    }
    struct EDGE
    {
        int to,l,a;
        EDGE *las;
    } e[MAXM*2];
    int ne;
    EDGE *last[MAXN+1];
    void link(int u,int v,int l,int a)
    {
        e[ne].to=v,e[ne].l=l,e[ne].a=a;
        e[ne].las=last[u];
        last[u]=e+ne;
        ++ne;
    }
    long long dis[MAXN+1];
    void Shortest_Path();
    int Q,K,S;
    struct Change_List
    {
        int x,a;
        long long ans;
    } cl[MAXN*2+1];
    int ncl;
    bool cmpcl(const Change_List &u,const Change_List &v)
    {
        return u.x<v.x || u.x==v.x && (u.a>v.a || u.a==v.a && u.ans>v.ans);
    }
    int ac[MAXN+1];
    struct Union_Find_Set
    {
        int fa;
        int a;
        long long ans;
        int dep;
    } ufs[MAXN+1];
    int getfa(int x)
    {
        while (ufs[x].fa!=x)
            x=ufs[x].fa;
        return x;
    }
    int main()
    {
    //  freopen("return.in","r",stdin);
    //  freopen("return.out","w",stdout);
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        int T;
        scanf("%d",&T);
        while (T--)
        {
            scanf("%d%d",&n,&m);
            memset(last,0,sizeof last);
            ne=0;
            for (int i=1;i<=m;++i)
            {
                int u,v,l,a;
                scanf("%d%d%d%d",&u,&v,&l,&a);
                link(u,v,l,a);
                link(v,u,l,a);
                ed[i].u=u,ed[i].v=v,ed[i].l=l,ed[i].a=a;
            }
            scanf("%d%d%d",&Q,&K,&S);
            Shortest_Path();
            sort(ed+1,ed+m+1,cmped);
            for (int i=1;i<=n;++i)
            {
                ufs[i].fa=i;
                ufs[i].a=S+1;
                ufs[i].ans=dis[i];
                ufs[i].dep=1;
            }
            ncl=0;
            for (int i=1;i<=n;++i)
            {
                ++ncl;
                cl[ncl].x=i;
                cl[ncl].ans=dis[i];
                cl[ncl].a=S+1;
            }
            for (int i=1;i<=m;++i)
            {
                int x=getfa(ed[i].u),y=getfa(ed[i].v);
                if (x!=y)
                {
                    //x->y
                    if (ufs[x].dep>ufs[y].dep)
                        swap(x,y);
                    ufs[x].fa=y;
                    ufs[x].a=ed[i].a;
                    ufs[y].dep=max(ufs[y].dep,ufs[x].dep+1);
                    if (ufs[x].ans<ufs[y].ans)
                    {
                        ufs[y].ans=ufs[x].ans;
                        ++ncl;
                        cl[ncl].x=y;
                        cl[ncl].ans=ufs[x].ans;
                        cl[ncl].a=ed[i].a;
                    }
                }
            }
            sort(cl+1,cl+ncl+1,cmpcl);
            for (int i=1,j=0;i<=ncl;++i)
                if (cl[i-1].x!=cl[i].x)
                    ac[j++]=i-1;
            ac[n]=ncl;
            long long lastans=0;
            while (Q--)
            {
                int v,p;
                scanf("%d%d",&v,&p);
                v=(v+K*lastans-1)%n+1;
                p=(p+K*lastans)%(S+1);
                while (v!=ufs[v].fa && p<ufs[v].a)
                    v=ufs[v].fa;
                int l=ac[v-1]+1,r=ac[v],res=-1;
                while (l<=r)
                {
                    int mid=l+r>>1;
                    if (p<cl[mid].a)
                        l=(res=mid)+1;
                    else
                        r=mid-1;
                }
                lastans=cl[res].ans;
                printf("%lld
    ",lastans);
            }
        }
        return 0;
    }
    bool cmph(int son,int fa)
    {
        return dis[son]>dis[fa];
    }
    int h[MAXN],nh;
    bool bz[MAXN+1];
    void Shortest_Path()
    {
        memset(dis+1,127,sizeof(long long)*n);
        memset(bz+1,0,sizeof(bool)*n);
        dis[1]=0;
        nh=1;
        h[0]=1;
        bz[1]=1;
        while (nh)
        {
            int top=h[0];
            pop_heap(h,h+nh--,cmph);
            for (EDGE *ei=last[top];ei;ei=ei->las)
                if (dis[top]+ei->l<dis[ei->to])
                {
                    dis[ei->to]=dis[top]+ei->l;
                    if (!bz[ei->to])
                    {
                        bz[ei->to]=1;
                        h[nh++]=ei->to;
                        push_heap(h,h+nh,cmph);
                    }
                }
            bz[top]=0;
        }
    }

    Others

    1、 我比赛时没切题,是因为数组开小了……
    2、 其实这个程序还有改进的地方,比如,在这题中,一些边的海拔是相同的,但我在最大生成树是会记录多条修改信息,其实可以并在一起。改起来也简单,但我懒得改了。

  • 相关阅读:
    Parcel与Parcelable剖析
    Binder文集
    Charles 使用教程
    AsyncTask
    Android 编译时注解
    scanf(),gets(),getchar()
    银行家算法
    最长公共子序列(LCS)问题
    动态规划 求解数字三角形最大值
    参数 存在二维数组
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145285.html
Copyright © 2020-2023  润新知