1. 程式人生 > >HNOI 2010 物品排程 並查集 置換

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 }
置換