zoukankan      html  css  js  c++  java
  • 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();
        }
    }
  • 相关阅读:
    链表--判断一个链表是否为回文结构
    矩阵--“之”字形打印矩阵
    二叉树——平衡二叉树,二叉搜索树,完全二叉树
    链表--反转单向和双向链表
    codeforces 490C. Hacking Cypher 解题报告
    codeforces 490B.Queue 解题报告
    BestCoder19 1001.Alexandra and Prime Numbers(hdu 5108) 解题报告
    codeforces 488A. Giga Tower 解题报告
    codeforces 489C.Given Length and Sum of Digits... 解题报告
    codeforces 489B. BerSU Ball 解题报告
  • 原文地址:https://www.cnblogs.com/candy99/p/6485309.html
Copyright © 2011-2022 走看看