zoukankan      html  css  js  c++  java
  • [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
    ",ans);
    78     }
    79     return 0;
    80 }
  • 相关阅读:
    Latex 双栏模式下表格太长怎么办?
    HTTP状态码大全
    You can't specify target table 'Person' for update in FROM clause
    mysql实战笔记
    「2020年中总结」这半年我又做了哪些副业?
    Mysql导入数据报错SQL Error(1153)
    PS制作电子签名
    Windows Server 2012 R2安装mssql
    Windows Server 2012 R2安装.net3.5
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/D-O-Time/p/8001518.html
Copyright © 2011-2022 走看看