• BZOJ 1998: [Hnoi2010]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最小的。 这样你得到了所有盒子的最终位置,现在你每次可以把某个盒子移动到空位上,移动后原盒子所在的位置成为空位。请问把所有的盒子移动到目的位置所需的最少步数。


    研究了好长时间那个并查集是怎么用的,照着黄学长的代码一直看,最后得出一个结论:这也太乱搞了...

    首先知道$pos$后就太容易做了置换群套路题

    对于$len>1$的循环按有没有$0$分类

    怎么算$pos$?

    容易发现$y$最多$n$种取值,$x$每$+1$就是在环上移动$d$个位置

    然后用并查集黑科技维护这个东西.....$fa$指向下一个可用位置,这个$y$上没可用位置就到$y+1$上去

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    const int N=1e5+5;
    typedef long long ll;
    inline int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    int n,s,q,p,m,d;
    ll c[N];
    int pos[N],fa[N],cir[N];
    bool vis[N],full[N];
    int find(int x){
        if(!full[cir[x]]) return x==fa[x]?x:fa[x]=find(fa[x]);
        else return find(fa[x]=(x+1)%n);
    }
    void solve(){
        for(int i=1;i<n;i++) c[i]=(c[i-1]*q+p)%m;
        for(int i=0;i<n;i++) c[i]%=n,fa[i]=i,cir[i]=-1,vis[i]=0,full[i]=0;
        for(int i=0;i<n;i++) 
            for(int j=i;cir[j]==-1;j=(j+d)%n) cir[j]=i;
        fa[s]=(s+d)%n;
        pos[0]=s;
        if(d==0) full[cir[s]]=1;
        for(int i=1;i<n;i++){
            int x=find(c[i]),y=find((x+d)%n);
            pos[i]=x;
            if(x==y) full[cir[x]]=1;
            else fa[x]=y;
        }
        int ans=0;
        for(int i=0;i<n;i++) if(!vis[i]){//printf("
    hi %d ",i);
            int u=pos[i],len=1;
            while(u!=i){//printf("%d ",u);
                vis[u]=1;
                len++;
                u=pos[u];
            }
            //printf("len %d
     ",len);
            if(len>1){
                if(i==0) ans+=len-1;
                else ans+=len+1;
            }
        }
        printf("%d
    ",ans);
    }
    int main(){
        freopen("in","r",stdin);
        int T=read();
        while(T--){
            n=read();s=read();q=read();p=read();m=read();d=read()%n;
            solve();
        }
    }
  • 相关阅读:
    mysql 主从配置 读写分离
    interface接口
    http结构
    call_user_func函数
    pcntl_fork 进程
    数据库事务
    php 之 ob缓冲
    shell脚本
    php 守护进程
    ssdb zset
  • 原文地址:https://www.cnblogs.com/candy99/p/6485309.html
Copyright © 2020-2023  润新知