zoukankan      html  css  js  c++  java
  • [BZOJ1998][Hnoi2010]Fsk物品调度

    [BZOJ1998][Hnoi2010]Fsk物品调度

    试题描述

    现在找工作不容易,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最小的。 这样你得到了所有盒子的最终位置,现在你每次可以把某个盒子移动到空位上,移动后原盒子所在的位置成为空位。请问把所有的盒子移动到目的位置所需的最少步数。

    输入

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

    输出

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

    输入示例

    1
    8 3 5 2 7 4

    输出示例

    6

    数据规模及约定

    t<=20,n<=100000

    题解

    刚刚在火车上调出了这道题。。。

    关键在于如何求 pos 数组,不难发现式子中 d 是固定的,所以 xi 每增加 1,posi 就要增加 d,而 yi 每增加 1,posi 会增加 1.

    再看看题目要求优先考虑令 yi 最小,即,固定 yi,改变 xi.假设我们已经把 yi 固定下来了,则可以将 1~n 的数按照对 d 取余得到的余数分类,通过 ci 和固定下来的 yi 的值确定要找的数在哪类,选取没有被选过的且离 ci “向右距离”(从 ci 出发向右走,遇到 n 就回到 0,继续向右到达该数所需的步数)最近的那个数就行了,可以用个并查集实现。那么如何固定 yi 呢?类似地,也可以对于每一数建立一个并查集,当某一类数都被选取后,将该节点与左右合并,查找时找没有满的且离 yi + ci 所属的类“向右距离”最近的一类即可。

    最后求步数不妨放自己yy。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    #define maxn 100010
    #define LL long long
    int n, s, q, p, m, d, gcdnd, pos[maxn];
    bool has[maxn], h2[maxn];
    
    int fa[maxn], siz[maxn];
    int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); }
    int f2[maxn];
    int find2(int x) { return x == f2[x] ? x : f2[x] = find2(f2[x]); }
    int nxt(int x) { return (x + d) % n; }
    int pre(int x) { return (x - d + n) % n; }
    LL gcd(LL a, LL b) { return !b ? a : gcd(b, a % b); }
    void add(int u, int x) {
    	has[u] = 1;
    	int v = findset(nxt(u));
    	if(has[v]) fa[u] = v;
    	v = findset(pre(u)); u = findset(u);
    	if(u != v) { if(has[v]) fa[v] = u; }
    	else {
    		h2[x] = 1; int xx = find2(x);
    		if(x < gcdnd - 1 && h2[x+1]){ v = find2(x + 1); if(x != v) f2[x] = v; }
    		if(x && h2[x-1]){ v = find2(x - 1); if(x != v) f2[v] = x; }
    	}
    	return ;
    }
    
    int main() {
    	int T; scanf("%d", &T);
    	while(T--) {
    		scanf("%d%d%d%d%d%d", &n, &s, &q, &p, &m, &d); d %= n;
    //		n = read(); s = read(); q = read(); p = read(); m = read(); d = read() % n;
    		gcdnd = gcd(n, d);
    		memset(has, 0, sizeof(has));
    		memset(h2, 0, sizeof(h2));
    		has[s] = 1;
    		for(int i = 0; i < n; i++) fa[i] = i;
    		for(int i = 0; i < gcdnd; i++) f2[i] = i;
    		LL c = 0;
    		for(int i = 1; i < n; i++) {
    			c = (c * q + p) % m;
    			LL cd = c % n % gcdnd;
    			int y = find2(cd); if(h2[y]) y++; if(y >= gcdnd){ y = find2(0); if(h2[y]) y++; }
    			int u;
    			if(y - cd >= 0){ u = findset((c % n + y - cd) % n); if(has[u]) u = nxt(u); }
    			else { u = findset((c % n + y - cd + gcdnd) % n); if(has[u]) u = nxt(u); }
    			add(u, y); pos[i] = u;
    		}
    		 
    //		for(int i = 1; i < n; i++) printf("%d ", pos[i]); puts("
    ");
    		for(int i = 0; i < n; i++) fa[i] = i, siz[i] = 1;
    		for(int i = 1; i < n; i++) {
    			int u = findset(i), v = findset(pos[i]);
    			if(u != v) fa[v] = u, siz[u] += siz[v], siz[v] = 0;
    		}
    		for(int i = 0; i < n; i++) pos[i] = findset(i);
    		sort(pos, pos + n);
    		int ans = 0;
    		for(int i = 0; i < n; i++) if((!i || pos[i] != pos[i-1]) && siz[pos[i]] > 1) ans += siz[pos[i]] + 1;
    		if(siz[findset(0)] > 1) ans -= 2;
    		printf("%d
    ", ans);
    	}
    	 
    	return 0;
    }
    
  • 相关阅读:
    c++的一些个学习及总结
    简单回合制游戏流程
    c++11 符号修饰与函数签名、函数指针、匿名函数、仿函数、std::function与std::bind
    二叉搜索树、平衡二叉树、红黑树
    STL之空间配置器
    centos 7 安装mysql及常用操作
    生产服务器问题定位
    Linux常用命令
    Garbage First(G1) 垃圾收集器
    垃圾回收算法和垃圾收集器
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5626514.html
Copyright © 2011-2022 走看看