【bzoj1998】Fsk物品调度 置换群+并查集

来源:互联网 发布:手机屏幕直播录像软件 编辑:程序博客网 时间:2024/05/17 05:12

AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=1998

【题解】

首先我们来看如何求出posi

pos[i]=c[i]+d*x[i]+y[i]。 要求y[i]最小,若y[i]相同,则要求x[i]最小。

上式中d的取值不同,所得pos[i]的值构成了一个环,如果环还有位置没有被取,我们选取其中x[i]最小的。
如果没有位置可取,我们需要将y[i]+1,其实等效于将c[i]+1,这样,我们跳到了另一个环中,再执行同样的操作。

我们可以用并查集维护环之间路径以及环内路径。

求出posi后,就转化为了一个经典问题,将所得置换群循环分解,若循环节中有0,则ans+=len-1

否则ans+=len+1

这题细节较多,看代码吧

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>using namespace std;typedef long long ll;#define FILE "read"#define MAXN 100010#define up(i,j,n) for(ll i=j;i<=n;++i)#define dn(i,j,n) for(ll i=j;i>=n;--i)#define cmax(a,b) a=max(a,b)#define cmin(a,b) a=min(a,b)namespace INIT{char buf[1<<15],*fs,*ft;inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}inline ll read(){ll x=0,f=1;  char ch=getc();while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getc();}while(isdigit(ch))  {x=x*10+ch-'0';  ch=getc();}return x*f;}}using namespace INIT;ll T,q,p,m,d,s,n,a[MAXN],c[MAXN],f[MAXN],cir[MAXN],over[MAXN],vis[MAXN];ll find(ll x){if(!over[cir[x]]) return f[x]==x?x:f[x]=find(f[x]);else return find((x+1)%n);}void solve(){ll temp=0; up(i,1,n-1){temp=(temp*q+p)%m;c[i]=temp%n;}up(i,0,n-1)  f[i]=i,cir[i]=-1;   d%=n;up(i,0,n-1)  for(ll j=i;cir[j]==-1;j=(j+d)%n)  cir[j]=i;if(!d)  over[s]=1;else f[s]=(s+d)%n;up(i,1,n-1) {ll p=find(c[i]),q=find((p+d)%n);  a[p]=i;if(p==q)  over[cir[p]]=1;else f[p]=q;}a[s]=0;  int ans=0;up(i,0,n-1)if(!vis[i]){ll len(0),flag(0);for(ll j=i;!vis[j];j=a[j]){vis[j]=1; len++;  if(j==0) flag=1;  }if(len>1)  ans+=(flag?len-1:len+1);}printf("%d\n",ans);}void pre(){memset(vis,0,sizeof(vis));memset(over,0,sizeof(over));}int main(){freopen(FILE".in","r",stdin);freopen(FILE".out","w",stdout);T=read();while(T--){n=read();  s=read();  q=read();  p=read();  m=read();  d=read();pre();  solve();}return 0;}



1 0