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 }