zoukankan      html  css  js  c++  java
  • 【bzoj2959】长跑【LCT+并查集】

    长跑

    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。

    码了一整个下午,终于过了样例,结果AC了……这题样例好强大。。

    题解:
    “为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。” 这句话是关键。因此,路途中经过的任意一个强联通分量的所有点权之和要全部加上。于是我们就动态维护强联通分量。开2个并查集,一个保存的是原树之间的关系用来判断连通性,一个保存的是强联通分量的代表节点。至于为什么要用第一个并查集,原因是…加速。于是每连一条边u-v,就用第一个并查集判断一下u,v是否连通。如果没有,就直接连接,否则意味着图中出现了一个环。于是,我们把这个环上的所有节点(也就是原来u-v的路径上的所有点)的点权移到一个代表节点上,然后丢掉其他所有点,还要把所有点的第二个并查集的父亲赋值为代表节点,因为它们属于的强联同分量改变了。怎么删除不要的点呢?只要保证不会在各种操作的时候跳到不要的节点即可。具体实现时,只要把所有的fa改为find(fa)即可,就会跳到代表节点上。剩下两个操作都是经典操作。不知道有没有什么更快的方法,但可以肯定的是,我的代码常数巨大==
    代码:

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=150005;
    int n,m,op,u,v,fu0,fv0,fu1,fv1,pa[N][2],fa[N],ch[N][2],rev[N],val[N],sumv[N];
    int a[N],stk[N];
    int find(int u,int md){
        return u==pa[u][md]?u:pa[u][md]=find(pa[u][md],md);
    }
    bool isroot(int u){
        if(!fa[u]){
            return true;
        }
        int tmp=find(fa[u],1);
        return u!=ch[tmp][0]&&u!=ch[tmp][1];
    }
    int which(int u){
        return u==ch[find(fa[u],1)][1];
    }
    void pushup(int u){
        if(!u){
            return;
        }
        sumv[u]=val[u]+sumv[ch[u][0]]+sumv[ch[u][1]];
    }
    void reverse(int u){
        rev[u]^=1;
        swap(ch[u][0],ch[u][1]);
    }
    void downtag(int u){
        if(rev[u]){
            if(ch[u][0]){
                reverse(ch[u][0]);
            }
            if(ch[u][1]){
                reverse(ch[u][1]);
            }
            rev[u]=0;
        }
    }
    void pushdown(int u){
        stk[stk[0]=1]=u;
        for(;!isroot(u);u=fa[u]){
            stk[++stk[0]]=fa[u];
        }
        while(stk[0]){
            downtag(stk[stk[0]--]);
        }
    }
    void rotate(int x){
        int y=find(fa[x],1),z=find(fa[y],1),md=x==ch[y][1];
        if(y==ch[z][0]||y==ch[z][1]){
            ch[z][y==ch[z][1]]=x;
        }
        fa[x]=z;
        ch[y][md]=ch[x][!md];
        if(ch[y][md]){
            fa[ch[y][md]]=y;
        }
        ch[x][!md]=y;
        if(y){
            fa[y]=x;
        }
        pushup(y);
        pushup(x);
    }
    void splay(int u){
        pushdown(u);
        while(!isroot(u)){
            if(!isroot(fa[u])){
                rotate(which(fa[u])==which(u)?fa[u]:u);
            }
            rotate(u);
        }
    }
    void access(int u){
        for(int v=0;u;v=u,u=find(fa[u],1)){
            splay(u);
            ch[u][1]=v;
            pushup(u);
        }
    }
    void makeroot(int u){
        access(u);
        splay(u);
        reverse(u);
    }
    int dfs(int rt,int u){
        if(!u){
            return 0;
        }
        pa[u][1]=rt;
        return dfs(rt,ch[u][0])+dfs(rt,ch[u][1])+val[u];
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            pa[i][0]=pa[i][1]=i;
        }
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sumv[i]=val[i]=a[i];
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&op,&u,&v);
            if(op==1){
                if((fu1=find(u,1))!=(fv1=find(v,1))){
                    if((fu0=find(u,0))!=(fv0=find(v,0))){
                        makeroot(fu1);
                        fa[fu1]=fv1;
                        pa[fv0][0]=fu0;
                    }else{
                        makeroot(fu1);
                        access(fv1);
                        splay(fv1);
                        val[fu1]=dfs(fu1,fv1);
                        fa[fu1]=0;
                        ch[fu1][0]=ch[fu1][1]=0;
                        pushup(fu1);
                    }
                }
            }else if(op==2){
                splay(fu1=find(u,1));
                val[fu1]-=a[u];
                a[u]=v;
                val[fu1]+=a[u];
                pushup(fu1);
            }else{
                if(find(u,0)!=find(v,0)){
                    puts("-1");
                    continue;
                }
                fu1=find(u,1),fv1=find(v,1);
                makeroot(fu1);
                access(fv1);
                splay(fv1);
                printf("%d
    ",sumv[fv1]);
            }
        }
        return 0;
    }
  • 相关阅读:
    0722
    SGU
    预测一下吧
    0625
    0624
    0610
    0607
    0604
    poj2455Secret Milking Machine【二分 + 最大流】
    BZOJ3489: A simple rmq problem
  • 原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476910.html
Copyright © 2011-2022 走看看