HNOI 2010 物品调度 并查集 置换

题意:

  题意有点细,暂不概括。请仔细审题。

 

分析:

  我们先要把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\n",ans);
32     } return 0;
33 }
置换

 

posted @ 2018-12-28 11:50  杜宇一声  阅读(194)  评论(0编辑  收藏  举报