[HNOI2010] 物品调度 fsk

标签:链表+数论知识。

题解:

  对于这道题,其实就是两个问题的拼凑,我们分开来看。
  首先要求xi与yi。这个可以发现,x每增加1,则pos增加d;y每增加1,则pos增加1。然后,我们把x与y分别写在二维平面上,比如样例:
    x= 0  1
y=0  {0  4}
y=1  {1  5}
y=2  {2  6}
y=3  {3  7}
  发现行数=gcd(n,d),列数=n/gcd(n,d)。
  那么题目要求y尽量小,然后x尽量小。我们先求y,那么初始pos就是ci,把ci放入这张表,寻找最近的可以不重复的行,也就是y。找到之后我们再把ci+y代入,去找在第y行的哪一个数是最近的,这样就能找到x与y了。
  具体的:我们使用rx[i],代表x=i时,右边那个可用的是哪一个,rx_cnt[i]代表距离。同样ry[i],与ry_cnt[i]分别表示y=i的下面的哪一个与距离。每次修改(也就是标记Val已经被使用)仅仅需要对于(Val+d)%n即可,然后rx[Val]=(Val+d)%n,rx_cnt[Val]=1。将Val%=gcd,即可完成对于y的修改。
  修改那么简单,那么查询:类似并查集一样的进行路径压缩,即如果rx[Val]被使用,那么rx[Val]=find(rx[Val]),对应修改rx_cnt[Val]即可,然后返回rx_cnt[Val]为x的值,y同样如此。
  解决了这个问题,下面求解最小步数就很简单了。我们在纸上将i与pos[i]连上一条边,发现会构成很多个环,如果这个环中有0,那么直接把0按照环的反方向依次移动,即可把整个环都归位,如果环中没有0,那么先把0移进去,归位之后再移出来,步数+2即可,这样是最优的方案。证明可以使用置换群什么的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<algorithm>
 5 #define LL long long
 6 using namespace std;
 7 const int MAXN=110000;
 8 bool vis[MAXN];
 9 int T,n,s,q,p,m,d,ans;
10 int c[MAXN],pos[MAXN],cnt[MAXN],rx[MAXN],ry[MAXN],rx_cnt[MAXN],ry_cnt[MAXN];
11 inline int gi(){int res; scanf("%d",&res); return res;}
12 int gcd(int A,int B)
13 {
14   if(A%B==0)return B;
15   return gcd(B,A%B);
16 }
17 void insert(int x)
18 {
19   int tmp;
20   if((tmp=x+d)>=n)tmp-=n;
21   rx[x]=tmp;
22   rx_cnt[x]=1;
23   x%=m;
24   if(--cnt[x])return;
25   if((tmp=x+1)>=m)tmp-=m;
26   ry[x]=tmp;
27   ry_cnt[x]=1;
28 }
29 int query(int *r,int *r_cnt,int x)
30 {
31   if(r[x]==x)return 0;
32   r_cnt[x]=r_cnt[x] + query(r,r_cnt,r[x]);
33   r[x]=r[r[x]];
34   return r_cnt[x];
35 }
36 int main()
37 {
38   T=gi();
39   while(T--)
40     {
41       scanf("%d%d%d%d%d%d",&n,&s,&q,&p,&m,&d); pos[0]=s;
42       for(int i=1;i<n;i++) c[i]=((LL)c[i-1]*q+p)%m;
43       d%=n;//insert()中没有使用取模,而是使用减法代替,所以这里先模一下。
44       m=gcd(n,d);
45       for(int i=0;i<m;i++)
46         {
47           cnt[i]=n/m;
48           ry[i]=i;
49           ry_cnt[i]=0;
50         }
51       for(int i=0;i<n;i++) rx[i]=i,rx_cnt[i]=0;
52       insert(pos[0]);
53       for(int i=1;i<n;i++)
54         {
55           int y=query(ry,ry_cnt,c[i]%m);
56           int x=query(rx,rx_cnt,(c[i]+y)%n);
57           pos[i]=((LL)d*x+y+c[i])%n;
58           insert(pos[i]);
59         }
60       ans=0; memset(vis,0,sizeof vis);
61       for(int i=0;i<n;i++)
62         if(!vis[i])
63           {
64             int now=i,Start=i,flag=0,cnt=0;
65             do
66               {
67                 vis[now]=1; cnt++;
68                 if(now==0)flag=1;
69                 now=pos[now];
70               }while(now!=Start);
71             if(cnt>1)
72               {
73                 ans+=cnt-1;
74                 if(!flag)ans+=2;
75               }
76           }
77       printf("%d\n",ans);
78     }
79     return 0;
80 }
posted @ 2017-12-07 23:08  D_O_Time  阅读(177)  评论(0编辑  收藏  举报