[HNOI2010 Fsk]

[关键字]:置换群 路径压缩

[题目大意]:生成数列c0..n-1: c0=0,  ci+1=(ci*q+p) MOD m。你需要确定非负整数x1..n-1, y1..n-1。使得pos0=s, posi=(ci+d*xi+yi) MOD n恰好是一个0到n-1的排列,当(xi,yi)有多种选择时,yi要尽量小,然后xi尽量小。通过这种方法我们可以得出唯一的pos0..n-1。假设有n-1个物品1..n-1 posi表示物品i的位置 pos0表示空位的位置。初始时空位在0号位,物品i在i号位。每次移动可以把一个物品放到空位去,然后它原来所在的位置变成空位。问从初始移动到目标pos最少需要多少移动。

//====================================================================================================================================

[分析]:首先第二问是一个经典的置换群的问题,如果一个置换群的循环s里有0,则需要|s|-次如果没有0则需要|s|-1+2(0换进换出)。而第一问需要用调整的方法,注意到根据公式x每加一就会后移d%n,y每加一就会后移1%n。要求y最小可以先从x开始调整,x所有能到达得位置有n/gcd(d%n,n)个所以对x的调整相当于在这个环里选出没有用过的数,而y则是在x的同余类的环里跳跃,一个环用完了就变到下一个环。利用类似并查集的压缩和查找可以降低复杂度。详细见HNOI2010题解压缩包。

[代码]:

View Code
#include<iostream>

#include<cstdio>

#include<cstdlib>

#include<cstring>

#include<algorithm>

using namespace std;



const int MAXN=120100;



int n,m,s,q,p,d,test;

int c[MAXN],pos[MAXN],x[MAXN],y[MAXN],a[MAXN];

int rx[MAXN],ry[MAXN],rx_cnt[MAXN],ry_cnt[MAXN],cnt[MAXN];

bool b[MAXN];



int GCD(int a,int b)

{

    if (a<b) swap(a,b);

    int t;

    while (b) t=a,a=b,b=t%b;

    return a;

}



void Work(int x)

{

     int x2;

     if ((x2=x+d)>=n) x2-=n;

     rx[x]=x2;

     rx_cnt[x]=1;

     x%=m;

     if (--cnt[x]) return;

     if ((x2=x+1)>=m) x2-=m;

     ry[x]=x2;

     ry_cnt[x]=1;

}



int Find(int *r,int *r_cnt,int x)

{

    if (r[x]==x) return 0;

    r_cnt[x]+=Find(r,r_cnt,r[x]);

    r[x]=r[r[x]];

    return r_cnt[x];

}



int main()

{

    freopen("in.txt","r",stdin);

    freopen("out.txt","w",stdout);

    scanf("%d",&test);

    while (test--)

    {

          scanf("%d%d%d%d%d%d",&n,&s,&q,&p,&m,&d);

          c[0]=0;

          for (int i=1;i<n;++i)

              c[i]=((long long)c[i-1]*q+p)%m;

          d%=n;

          m=GCD(d,n);

          for (int i=0;i<m;++i)

          {

              cnt[i]=n/m;

              ry[i]=i;

              ry_cnt[i]=0;

          }

          for (int i=0;i<n;++i)

              rx[i]=i,rx_cnt[i]=0;

          Work(pos[0]=s);

          for (int i=1;i<n;++i)

          {

              y[i]=Find(ry,ry_cnt,c[i]%m);

              x[i]=Find(rx,rx_cnt,(c[i]+y[i])%n);

              pos[i]=(c[i]+(long long)x[i]*d+y[i])%n;

              Work(pos[i]);

          }

          for (int i=0;i<n;++i)

              a[i]=pos[i];

          int ans=0;

          memset(b,0,sizeof(b));

          for (int i=0;i<n;++i)

              if (!b[i])

              {

                        int t=0,p=i;

                        while (!b[p]) b[p]=1,++t,p=a[p];

                        if (t!=1)

                           ans+=t-1+(i!=0)*2;

              }

          printf("%d\n",ans);

    }

    fclose(stdin),fclose(stdout);

    return 0;

}

你可能感兴趣的:([HNOI2010 Fsk])