题意:
题意有点细,暂不概括。请仔细审题。
分析:
我们先要把c生成出来。
记得颜神讲这道题,首先表明,这道题有两个问题需要处理。
第一个是要先定位,第二个是要求最小移动步数。
定位时对于每一个物品i,要在不与之前物品冲突的基础上保证y最小,然后x最小。
可以想到,如果没有c,当y一定时,枚举x就相当于在一个环上不断向后移动d个位置,可以想到,当枚举到一定程度时,会回到原来的位置。
这样呢,为了不冲突,我们就只能利用调整y来摆脱窘境。
所以一个思路就出来了,我们从小到大枚举y,对于每个y,我们把满足(ci+d*xi+yi) mod n的位置都不重复地占满,此时就要继续让y变大再寻找空位了。
序列c存在的意义是什么???我想仅仅是为了使这一步没有数学规律吧……
这样呢,我们就确定了一个终序列,此时就需要拿出置换相关的知识,来求最小步数了。
因为我们只有一个空位可起到容器的作用,两个实际的物品也不能直接交换,所以容易发现,一个置换要想完成,就必须要完成若干个类似环的操作(相当于空位在环上走)。但是,如果环上没有空位怎么办??
就要分类讨论。
首先,如果我们找到的一个环上有空位,那么这个环产生的代价最小是环长-1(因为有个空位)
但是如果环上没有空位,那么产生的代价是环长+1(因为要把空位先换过来,产生1的代价,最终要把空位归位或者哪来的环会哪去,又产生1的代价)
致此,这道题就差不多做完了。
代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int N=100005; 5 bool v[N];ll c[N];int s,d; 6 int fa[N],pos[N],t,n,m,p,q; 7 int get(int x){ 8 return fa[x]==x?x:fa[x]=get(fa[x]); 9 } int main(){ 10 scanf("%d",&t); 11 while(t){ t--; 12 scanf("%d%d%d%d%d%d",&n,&s,&q,&p,&m,&d); 13 for(int i=0;i<n;i++) fa[i]=i,v[i]=0; 14 pos[0]=s;v[s]=1;fa[s]=(s+d)%n; 15 for(int i=1;i<n;i++) c[i]=(c[i-1]*q+p)%m; 16 for(int i=1;i<n;i++){ 17 int y=0; 18 while(v[get((y+c[i])%n)]) y++; 19 pos[i]=get((y+c[i])%n); 20 v[pos[i]]=1; 21 fa[pos[i]]=get((pos[i]+d)%n); 22 } memset(v,0,sizeof(v));int ans=0; 23 for(int i=0;i<n;i++){ 24 if(v[i]||pos[i]==i) continue; 25 int cur=i,l=0;bool bs=0; 26 while(!v[cur]){ 27 if(!cur) bs=1;l++; 28 v[cur]=1;cur=pos[cur]; 29 } if(bs) ans+=(l-1); 30 else ans+=(l+1); 31 } printf("%d ",ans); 32 } return 0; 33 }