• 【BZOJ2959】长跑 (LCT+并查集)


     

    Time Limit: 1000 ms   Memory Limit: 256 MB

    Description

      某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间  操场上熙熙攘攘,摩肩接踵,盛况空前。
      为了让同学们更好地监督自己,学校推行了刷卡机制。
      学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
      有以下三类事件:
      1、修建了一条连接A地点和B地点的跑道。
      2、A点的刷卡机台数变为了B。
      3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
      当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
      为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。

    Input

      输入的第一行包含两个正整数n,m,表示地点的个数和操作的个数。
      第二行包含n个非负整数,其中第i个数为第个地点最开始刷卡机的台数。
      接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。
      最初所有地点之间都没有跑道。
      每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。

    Output

      输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。

    Sample Input And Output Are Too Long>_<

    Hint 

      对于100%的数据,m<=5n,任意时刻,每个地点的刷卡机台数不超过10000。

      具体每组数据的规模如下

      $30\%: n leq 5000 $

      $100\%: 1 leq n leq 150000, 1 leq m leq 5n, 0 leq v_i leq 10000$


    题解

      PS:我原来以为是有向图结果不会做,后来发现我被题面坑了......

      题意是动态维护加入无向边,修改点权的操作;询问对每条边设置方向之后,A到B的点权之和最大值。

      如果某一些点构成了双联通分量,那么可以考虑将这些点缩成一个点,其点权为所有点之和,原本连向这些点的边都连向代表点。

      那么如果支持动态缩点,那么整个图保持为一棵树,询问A到B即询问树上A所属代表元到B所属代表元的点权和,这可以用LCT维护。

      那么看看怎么缩点:

      用一个并查集维护每一个点的代表元,怎么维护?

      考虑每一次连边$(u,v)$:

        首先我们把$u$赋值为$u$的代表元,$v$赋值为$v$的代表元。

        1. 如果$u$和$v$原本不连通,那么直接连上。

           2.如果$u$和$v$原本已经连通,需要将$u$到$v$的路径缩成一个点,那么就在LCT上提取出$u$到$v$的路径,makeroot(u);access(v);splay(v);放到一棵Splay里就好。干脆就把v当做代表元好了,那么v自带的信息已经是所有点的权值之和,接下来遍历一遍这一棵Splay,将所有点的并查集父亲设置为$v$,就完成了缩点。

      

      那么对于LCT,缩点是怎么体现的呢?

      在并查集已经维护好的情况下,我们每次$access(u)$时,原本是迭代为父亲,现在直接迭代为父亲的代表元。为什么这样是对的?对于一棵已经缩点的Splay,从下面的别的Splay$access$上来的时候,不可以再对这些已经缩了的点进行操作了,直接找到代表元来操作。由此,$access$的迭代不再是连续的了;若当前点为$u$,上一个点为$v$,那么要额外修正$v$的父亲为$u$。

         这样维护,对于LCT自身的维护是没有影响的,所以不用操心啦。


    #include <cstdio>
    using namespace std;
    typedef long long ll;
    const int N=150010;
    int n,m,bl[N];
    int ch[N][2],fa[N],rev[N];
    ll w[N],val[N],sum[N];
    inline ll rd(){
        ll x=0;
        char c;
        while((c=getchar())<'0'||c>'9');
        x=c-'0';
        while('0'<=(c=getchar())&&c<='9') x=x*10+c-'0';
        return x;
    }
    inline int find(int x){return bl[x]==x?x:bl[x]=find(bl[x]);};
    inline void swap(int &x,int &y){int t=x;x=y;y=t;}
    inline bool isRoot(int u){return ch[fa[u]][0]!=u&&ch[fa[u]][1]!=u;}
    inline bool who(int u){return ch[fa[u]][1]==u;}
    inline void reverse(int u){
        rev[u]^=1;
        swap(ch[u][0],ch[u][1]);
    }
    inline void pushup(int u){
        sum[u]=sum[ch[u][0]]+sum[ch[u][1]]+val[u];
    }
    inline void rotate(int u){
        int f=fa[u],g=fa[f],c=who(u);
        if(!isRoot(f)) ch[g][who(f)]=u;
        fa[u]=g;
        ch[f][c]=ch[u][c^1];
        if(ch[f][c]) fa[ch[f][c]]=f;
        ch[u][c^1]=f;
        fa[f]=u;
        pushup(f); 
        pushup(u);
    }
    inline void pd(int u){
        if(rev[u]){
            if(ch[u][0]) reverse(ch[u][0]);
            if(ch[u][1]) reverse(ch[u][1]);
            rev[u]=0;
        }
    }
    inline void pushdown(int u){
        if(!isRoot(u)) pushdown(fa[u]);
        pd(u);
    }
    inline void splay(int u){
        pushdown(u);
        while(!isRoot(u)){
            if(!isRoot(fa[u]))
                rotate(who(fa[u])==who(u)?fa[u]:u);
            rotate(u);
        }
    }
    inline void access(int u){
        for(int v=0;u;v=u,u=find(fa[u])){
            splay(u);
            ch[u][1]=v;
            fa[v]=u;
            pushup(u);
        }
    }
    inline void makeRoot(int u){
        access(u);
        splay(u);
        reverse(u);
    }
    inline bool isConnect(int a,int b){
        if(a==b) return true;
        makeRoot(a);
        access(b);
        splay(b);
        return fa[a];
    }
    void shrink(int u,int target){
        if(!u) return;
        bl[u]=target;
        shrink(ch[u][0],target);
        shrink(ch[u][1],target);
    }
    inline void link(int a,int b){
        a=find(a); b=find(b);
        if(a==b) return;
        if(isConnect(a,b)){
            makeRoot(a);
            access(b);
            splay(b);
            shrink(b,b);
            val[b]=sum[b];
        }
        else{
            makeRoot(a);
            fa[a]=b;
        }
    }
    inline void change(int a,int b){
        ll delta=b-w[a];
        w[a]=b;
        a=find(a);
        val[a]+=delta;
        sum[a]+=delta;
        splay(a);
    }
    inline ll query(int a,int b){
        a=find(a); b=find(b);
        if(!isConnect(a,b)) return -1;
        if(a==b) return val[a];
        makeRoot(a);
        access(b);
        splay(b);
        return sum[b];
    }
    int main(){
        n=rd(); m=rd();
        for(int i=1;i<=n;i++) w[i]=val[i]=rd();
        for(int i=1;i<=n;i++) bl[i]=i;
        int opt,a,b;
        while(m--){
            opt=rd(); a=rd(); b=rd();
            switch(opt){
                case 1: link(a,b); break;
                case 2: change(a,b); break;
                case 3: printf("%lld
    ",query(a,b)); break;
            }
        }
        return 0;
    }
    奇妙代码
  • 相关阅读:
    如何更好地理解闭包
    抽象类和抽象方法以及和接口区别
    JavaScript中如何理解如何理解Array.apply(null, {length:5})
    Java线程中的同步
    Python前世今生以及种类、安装环境
    大数据中的用户画像
    Java web每天学之Servlet工作原理详情解析
    Go语言操作MySQL数据库
    老集群RAC双网卡绑定
    nmcli配置ipv6
  • 原文地址:https://www.cnblogs.com/RogerDTZ/p/7993071.html
Copyright © 2020-2023  润新知