EOJ 1031 传球问题

EOJ 1031 http://acm.cs.ecnu.edu.cn/problem.php?problemid=1031

题意:

  传球问题,按题意我们得到编号从1~2~..~ i ~..~n~1的,那么去掉末尾的1(将环从n后剪开),

  我们得到:1~2~..~ i ~..~n,多了首尾不同的限制,于是问题完全等价 HDU 2045 —— 染色问题(做过这题的话,很容易想到)。

  HDU 2045  http://acm.hdu.edu.cn/showproblem.php?pid=2045

  具体分析:

  n为传球总次数,i 一个空位, 待填入接到第 i-1 次传球的人;

  又有p个人,记为x1,x2...,xp;按题意:编号1处只有一种选择,不妨填x1,而2~n可以有多种填法, 但须有相邻(包括首尾)的不同。

  以下用填格子指代传球。

  记f[n]为填到第 n 个格子时的总填法。那么分为两种:(n>=3)

  按照  第n-1个格子与首位  的关系;

  1.相同:第n-1个格子——1种填法=>第n个格子——p-1种;剩下前n-2个格子为子问题f[n-2]。

  2.不同:第n-1个格子——p-1种填法=>第n个格子——p-2种;

      (由于第n-1个格子与首位不同)这时,前n-1个格子本身即为子问题f[n-1]。

  这样容易写出递推关系:

  f[n] = (p-1)*f[n-2] + (p-2)*f[n-1];   

  题意要求结果对2005取模:那么只需:

  f[n] = ( ((p-1)%M) * f[n-2]  + ((p-2)%M) * f[n-1] )%M;  //M为2005

  此题的另一个要注意的是:n, p 都很大,不能暴力求解。  

  注意到 f[n] = (p-1)*f[n-2] + (p-2)*f[n-1]; <=>  f[n] = A*x^n + B*y^n; (A,B,x,y∈N)。

  由抽屉原理:设a[n] = (A*x^n)%M; M为某常数, 则a[n]一定循环(n>M 时),循环节最大为M;

  B*y^n同理, 所以f[n]循环节最大为M^2;

  代码如下:

  

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string>
 4 #include <algorithm>
 5 #include <string.h>
 6 #include <stdlib.h>
 7 #define INF 0x3f3f3f 
 8 #define MAX 2010
 9 #define M 2005
10 using namespace std;
11 short f[MAX*MAX];
12 void print(int *f)
13 {
14     for(int i=1; i<=10; i++)
15         cout << i << ". " << f[i] << endl;
16 }
17 int main()
18 {
19     //freopen("testin.txt", "r", stdin);
20     //freopen("testout.txt", "w", stdout);
21     
22     int p, n;
23     while(cin >> p >> n, p&&n)
24     {
25         int i;
26         f[1] = 0;
27         f[2] = (p-1)%M;
28         for(i=3; i<=n; i++)
29         {            
30             f[i] = (((p-1)%M)*f[i-2] + ((p-2)%M)*f[i-1])%M;
31             if(f[i-1] == f[1] && f[i] == f[2])
32                 break;    //当循环时即可跳出。 
33         }
34         //print(f);
35         if(i<n)        //循环 
36         {
37             int loop = i-2; 
38             int ans = n%loop;
39             if(n%loop == 0)
40                 ans = loop;
41             cout << f[ans] << endl;
42         }
43         else    //n较小,未循环  
44             cout << f[n] << endl;
45     } 
46     
47     
48     return 0;
49 }
View Code

  

原文地址:https://www.cnblogs.com/KimKyeYu/p/3132930.html