HDU5860 (递推)

Problem Death Sequence

题目大意

  排成一行的约瑟夫问题。

  n个人排成一行,从第一个人开始,每个k个人报数,报到数的人被杀死,剩下的人重新排成一行再报数。

  一共q个询问,每次询问第qi个死的人是谁。

  n <= 3000000 , q <= 1000000 , k>=1 。

解题分析

  显然每一轮游戏可以看做是一个子问题。

  假设编号为0~n-1,若某轮中某人的编号为i,如果i被k整除则被杀,否则在下轮中编号为i-i/k-1。

  dp[i]表示编号为i的人可以存活几轮,那么dp[i]=i % k ? dp[i-i/k-1] : 0 。

  再用一个数组s[i]记录一下前i轮一共死了多少人。

  这样就可以在O(n)时间内预处理出一个答案序列。

参考程序

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<algorithm>
 7 #include<string>
 8 #include<iomanip>
 9 #include<vector>
10 #include<set>
11 #include<map>
12 #include<queue>
13 
14 using namespace std;
15 typedef long long LL;
16 typedef unsigned long long ULL;
17 
18 #define rep(i,k,n) for(int i=(k);i<=(n);i++)
19 #define rep0(i,n) for(int i=0;i<(n);i++)
20 #define red(i,k,n) for(int i=(k);i>=(n);i--)
21 #define sqr(x) ((x)*(x))
22 #define clr(x,y) memset((x),(y),sizeof(x))
23 #define pb push_back
24 #define mod 1000000007
25 
26 const int maxn=3000300;
27 int n,k,q,tot;
28 int f[maxn],s[maxn],ans[maxn],a[maxn];
29 
30 void init()
31 {
32     int tmp=n;
33     tot=0;
34     s[0]=0;
35     while(tmp)
36     {
37         tot++;
38         s[tot]=s[tot-1]+(tmp-1)/k+1;
39         tmp-=(tmp-1)/k+1;
40     }
41     f[0]=0;
42     rep(i,0,n-1){
43         f[i]= i % k ? f[i-i/k-1]+1 : 0 ;
44         a[i]= i % k ? a[i-i/k-1] : i/k+1;
45     }
46     rep(i,0,n-1){
47         int tmp=s[f[i]]+a[i];
48         ans[tmp]=i;
49     }
50     //rep(i,1,n) printf("%d %d\n",i,ans[i]+1 );
51 }
52 
53 int main()
54 {
55     int t;
56     scanf("%d",&t);
57     while(t--)
58     {
59         scanf("%d%d%d",&n,&k,&q);
60         init();
61         while (q--){
62             int x;
63             scanf("%d",&x);
64             printf("%d\n",ans[x]+1);
65         }
66     }
67     
68     return 0;
69 }
View Code

 

posted @ 2016-08-18 23:21  rpSebastian  阅读(605)  评论(0编辑  收藏  举报