【bzoj1998】[Hnoi2010]Fsk物品调度

2 篇文章 0 订阅
1 篇文章 0 订阅

题目链接

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 < n。其余所有数字均为不超过100000的正整数。

Output

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

Sample Input

1

8 3 5 2 7 4

Sample Output

6

HINT

说明:第1个到第7个盒子的最终位置依次是:2 5 6 4 1 0 7
计算过程可能超过整型范围。

题解

po一下大佬的题解:
http://blog.163.com/benz_/blog/static/186842030201142352718885/

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

inline int read(){
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)) { if(c == '-') f = -1; c = getchar(); }
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int N = 100000 + 10;
int t, n, s, q, p, m, d, ans;
bool over[N], vis[N];
ll c[N];
int fa[N], nxt[N], cir[N];

inline int find(int x){
    if(!over[cir[x]]) return x == fa[x] ? x : fa[x] = find(fa[x]);
    return find(fa[x] = (x + 1) % n);
}

void init(){
    n = read(); s = read(); q = read(); p = read(); m = read(); d = read();
    ans = 0;
    memset(over, 0, sizeof(over));
    memset(vis, 0, sizeof(vis));
}

void solve(){
    d %= n;
    ll tmp = 0;
    for(int i = 1; i < n; i++){
        tmp = (tmp * q + p) % m;
        c[i] = tmp % n;
    }
    for(int i = 0; i < n; i++) fa[i] = i, cir[i] = -1;
    for(int i = 0; i < n; i++)
        for(int j = i; cir[j] == -1; j = (j + d) % n)
            cir[j] = i;
    if(!d) over[s] = 1;
    else fa[s] = (s + d) % n;
    for(int i = 1; i < n; i++){
        int pos = find(c[i]), f = find((pos + d) % n);
        nxt[pos] = i;
        if(pos == f) over[cir[pos]] = 1;
        else fa[pos] = f;
    }
    nxt[s] = 0;
    for(int i = 0; i < n; i++){
        if(!vis[i]){
            int l = 0, mk = 0;
            for(int k = i; !vis[k]; k = nxt[k]){
                l++;
                vis[k] = 1;
                if(k == 0) mk = 1;
            }
            if(l > 1)
                if(mk) ans += l-1;
                else ans += l+1;
        }
    }
}

void work(){
    t = read();
    while(t--){
        init();
        solve();
        printf("%d\n", ans);
    }
}

int main(){
    work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值