【BZOJ1998】[HNOI2010]物品调度(并查集,模拟)

【BZOJ1998】[HNOI2010]物品调度(并查集,模拟)

题面

BZOJ,为啥这题都是权限题啊?
洛谷

题解

先不管\(0\)位置是个空,把它也看成一个箱子。那么最终的答案显然和置换循环节的个数相关,对于大小为\(1\)的循环,显然就是不要动。对于大小为\(L\)的循环,如果包含\(0\)位置,显然还要动\(L-1\)次,如果不包含\(0\)位置,显然要先把\(0\)位置弄进这个环里面来,再把它移出去,也就是\(L+1\)次。那么我们就可以很容易的计算答案。
然而现在最大的难题变成了如何计算最终位置了。
对于每个位置,我们不难发现随着\(x\)的增大,关于\(n\)的余数一定是一个个的环。那么我们考虑枚举\(y\)值,检查\(y\)固定时,对应的这个环上是否还有空位,如果有就放进去。这个空位可以用并查集维护,每次将当前放完的位置指向下一个位置,即\(+d\)的位置。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 100100
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int C[MAX],pos[MAX];
int n,S,Q,P,M,D;
int f[MAX];bool vis[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
void merge(int u,int v){f[getf(u)]=getf(v);}
int main()
{
	int T=read();
	while(T--)
	{
		n=read();S=read();Q=read();P=read();M=read();D=read();
		for(int i=1;i<n;++i)C[i]=(1ll*C[i-1]*Q+P)%M;
		for(int i=0;i<n;++i)f[i]=i;
		memset(vis,0,sizeof(vis));
		pos[0]=S;vis[S]=true;merge(S,(S+D)%n);
		for(int i=1;i<n;++i)
		{
			int x=getf(C[i]%n),y=0;
			while(vis[x])++y,x=getf((C[i]+y)%n);
			pos[i]=x;vis[x]=1;merge(x,(x+D)%n);
		}
		memset(vis,0,sizeof(vis));int ans=0,gr=0;
		for(int i=0;i<n;++i)
			if(!vis[i])
			{
				int tot=0,u=i;
				while(!vis[u])++tot,vis[u]=true,u=pos[u];
				if(tot>1)ans+=tot,++gr;
			}
		if(!pos[0])ans+=gr;else ans+=gr-2;
		printf("%d\n",ans);
	}
	return 0;
}
posted @ 2018-10-24 22:46  小蒟蒻yyb  阅读(277)  评论(2编辑  收藏  举报