procedure2012
It's not worth it to know you're not worth it!

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

[题目大意]:生成数列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;
}
posted on 2012-04-10 12:05  procedure2012  阅读(262)  评论(0)    收藏  举报