[BZOJ 1998][Hnoi2010]Fsk物品调度

1998: [Hnoi2010]Fsk物品调度


Time Limit: 10 Sec  Memory Limit: 259 MB
Submit: 350  Solved: 168
[Submit][Status][Discuss]
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
计算过程可能超过整型范围。



并查集维护链表??



#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100020
using namespace std;

typedef long long ll;

int n, s, q, p, m, d;
bool over[maxn], done[maxn], bo[maxn];
int g[maxn], a[maxn], fa[maxn];
ll c[maxn];

int find(int x){
	if(!over[g[x]])return x == fa[x] ? x : fa[x] = find(fa[x]);
	if(!done[x])done[x] = 1, fa[x] = (x + 1) % n;
	if(over[g[fa[x]]]){
		int s = find(fa[x]);
		fa[x] = fa[fa[x]];
		return s;
	}
	return find(fa[x]);
}

void solve(){
	d %= n;
	
	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;
		g[i] = -1;
		over[i] = done[i] = 0;
	}
	for(int i = 0; i < n; i ++)
	    for(int j = i; g[j] < 0; j = (j + d) % n)
			g[j] = i;
	
	if(d == 0)over[s] = 1;
	else fa[s] = (s + d) % n;
	
	for(int i = 1; i < n; i ++){
		int p = find(c[i]);
		a[p] = i;
		int f = find((p + d) % n);
		if(p == f)over[g[f]] = 1;
		else fa[p] = f;
	}
	a[s] = 0;
	int ans = 0;
	memset(bo, 0, sizeof bo);
	for(int i = 0; i < n; i ++)
	    if(!bo[i]){
			int cnt = 0;
			int f = bo[0];
			for(int j = i; !bo[j]; j = a[j])
			    bo[j] = true, cnt ++;
			if(cnt > 1)
			    ans += bo[0] - f == 1 ? cnt - 1 : cnt + 1;
	    }
	printf("%d\n", ans);
}

int main(){
	int test;
	scanf("%d", &test);
	while(test --){
		scanf("%d%d%d%d%d%d", &n, &s, &q, &p, &m, &d);
		solve();
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值