1. 程式人生 > >【BZOJ 1998】[Hnoi2010]Fsk物品調度 置換群+並查集

【BZOJ 1998】[Hnoi2010]Fsk物品調度 置換群+並查集

register OS sca 情況 經典的 gist += hellip 路徑

置換群的部分水得一比,據說是經典的置換群理論(然而我並不知道這理論是啥).
重點就在於怎麽求pos!!!
容易發現這個東西是這樣的:每次尋找pos,先在本環裏找,找不到再往下一個環裏找,直到找到為止……
一開始我想二分或者是set,但是感覺會T,然後想了很久之後想到用並查集:
就是維護每一個被占用的位置的下一個位置,因為這個位置被占用之後就會轉向下一個位置,當然下一個位置有在環內部和在下一個環裏兩種情況,這兩種情況都我都是用並查集維護的,但是一定要註意,不要把這兩種情況寫成一個並查集,這樣路徑壓縮之後會出事,所以要對於這兩種情況分別維護兩個並查集.

#include <cstdio>
#include 
<cstring> #include <algorithm> typedef long long LL; const int N=100010; int n,d,c[N],pos[N],f1[N],f2[N]; bool vis[N]; inline int find1(int x){return f1[x]==x?x:f1[x]=find1(f1[x]);} inline int find2(int x){return f2[x]==x?x:f2[x]=find2(f2[x]);} inline int get(int x){ int ret=find2(find1(x));
if(find2((ret+d)%n)==ret){ f1[ret]=(ret+1)%n; for(int i=(ret+d)%n;i!=ret;i=(i+d)%n) f1[i]=(i+1)%n; }else f2[ret]=find2((ret+d)%n); return ret; } int main(){ register int i; int s,q,p,m,T,j,ans,size; bool yeah; scanf("%d",&T); while(T--){ scanf("%d%d%d%d%d%d
",&n,&s,&q,&p,&m,&d); c[0]=0,pos[0]=s,ans=0,d%=n; for(i=1;i<n;++i)c[i]=((LL)c[i-1]*q+p)%m; for(i=0;i<n;++i)c[i]%=n,vis[i]=false,f1[i]=f2[i]=i; get(s); for(i=1;i<n;++i)pos[i]=get(c[i]); for(i=0;i<n;++i){ if(vis[i])continue; vis[i]=true,yeah=i==0,size=1; for(j=pos[i];j!=i;j=pos[j]) vis[j]=true,++size,yeah=(yeah||(j==0)); if(size!=1)ans+=size+(yeah?-1:1); } printf("%d\n",ans); } return 0; }

【BZOJ 1998】[Hnoi2010]Fsk物品調度 置換群+並查集