【BZOJ 1998】[Hnoi2010]Fsk物品调度 置换群+并查集

置换群的部分水得一比,据说是经典的置换群理论(然而我并不知道这理论是啥).
重点就在于怎么求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;
}

 

posted @ 2018-03-06 21:32  TS_Hugh  阅读(208)  评论(0编辑  收藏  举报