zoukankan      html  css  js  c++  java
  • BZOJ_1998_[Hnoi2010]Fsk物品调度_并查集+置换

    BZOJ_1998_[Hnoi2010]Fsk物品调度_并查集+置换

    Description

    现在找工作不容易,Lostmonkey费了好大劲才得到fsk公司基层流水线操作员的职位。流水线上有n个位置,从0到n-1依次编号,一开始0号位置空,其它的位置i上有编号为i的盒子。Lostmonkey要按照以下规则重新排列这些盒子。 规则由5个数描述,q,p,m,d,s,s表示空位的最终位置。首先生成一个序列c,c0=0,ci+1=(ci*q+p) mod m。接下来从第一个盒子开始依次生成每个盒子的最终位置posi,posi=(ci+d*xi+yi) mod n,xi,yi是为了让第i个盒子不与之前的盒子位置相同的由你设定的非负整数,且posi还不能为s。如果有多个xi,yi满足要求,你需要选择yi最小的,当yi相同时选择xi最小的。 这样你得到了所有盒子的最终位置,现在你每次可以把某个盒子移动到空位上,移动后原盒子所在的位置成为空位。请问把所有的盒子移动到目的位置所需的最少步数。

    Input

    第一行包含一个整数t,表示数据组数。接下来t行,每行6个数,n,s,q,p,m,d意义如上所述。 对于30%的数据n<=100,对于100%的数据t<=20,n<=100000,s

    Output

    对于每组数据输出一个数占一行,表示最少移动步数。

    Sample Input

    1
    8 3 5 2 7 4

    Sample Output

    6

    HINT

    说明:第1个到第7个盒子的最终位置依次是:2 5 6 4 1 0 7


    一开始没看懂题....

    考虑先求出pos,题中已经给出了限制:如果有多个xi,yi满足要求,你需要选择yi最小的,当yi相同时选择xi最小的。

    暴力怎么做,在ci这个位置,先看y=0时有没有剩下的,再看y=1....,然后找到一个最小的x。

    相当于先+1+1+1再+d+d+d,考虑用并查集来维护删除过程。

    两个并查集分别维护+1和+d,当+d的并查集出现环,说明这个环的都取走了,就更新+1的那个并查集。

    pos求完了,对于每个环(大小为siz),如果环里有0,贡献为siz-1,否则贡献为siz+1。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define N 100050
    int n,s,q,p,m,d,C[N],f1[N],f2[N],pos[N],vis[N];
    int find1(int x) {return f1[x]==x?x:f1[x]=find1(f1[x]);}
    int find2(int x) {return f2[x]==x?x:f2[x]=find2(f2[x]);}
    int tx(int x) {return (x+d)%n;}
    int ty(int x) {return (x+1)%n;}
    void del(int x) {
        if(find1(tx(x))==x) {
            int y;
            f2[x]=find2(ty(x)); for(y=x;tx(y)!=x;y=tx(y),f2[y]=find2(ty(y))); 
        }else {
            f1[x]=find1(tx(x));
        }
    }
    void solve() {
        scanf("%d%d%d%d%d%d",&n,&s,&q,&p,&m,&d);
        int i;
        for(C[0]=0,i=1;i<n;i++) C[i]=(ll(C[i-1])*q+p)%m;
        for(i=1;i<n;i++) C[i]=C[i]%n,f1[i]=f2[i]=i; f1[0]=f2[0]=0;
        del(s); pos[0]=s;
        for(i=1;i<n;i++) {
            int p=C[i];
            pos[i]=find1(find2(p));
            del(pos[i]);
        }
        // for(i=0;i<n;i++) printf("%d
    ",pos[i]);
        memset(vis,0,sizeof(vis));
        int ans=0;
        for(i=0;i<n;i++) if(!vis[i]) {
            int j,siz=1,flg=(i==0);
            for(vis[j=i]=1;pos[j]!=i;j=pos[j],vis[j]=1,siz++) if(pos[j]==0) flg=1;
            if(siz==1) continue;
            if(flg) ans+=siz-1;
            else ans+=siz+1;
        }
        printf("%d
    ",ans);
    }
    int main() {
        int T;
        scanf("%d",&T);
        while(T--) solve();
    }
    
  • 相关阅读:
    android签名文件(.keystore)
    server2008部署网狐荣耀遇到的问题
    网关详解
    服务端架构
    rest service下载文件与上传
    mysql 按类别之用一条SQL语句查询出每个班前10名学生数据
    windows mysql 和linux mysql解决乱码问题
    关于redis的安装
    收藏nginx学习
    linux在tomcat中指定jdk
  • 原文地址:https://www.cnblogs.com/suika/p/9426921.html
Copyright © 2011-2022 走看看