题目链接:传送门
题解,思路写在代码里面。
递归:
#include <iostream> #include <cstring> #include <cstdio> using namespace std; int n, m; // 假设一个序列 一共 7人 A: 1, 2, 3, 4, 5, 6 ,7 // m = 3 // 第一次出列 队列只剩下5人: // 1, 2, 4, 5, 7 // 因为第二次要从尾部开始数: // 队列应为 B: 7, 5, 4, 2, 1 // BchangeintoA() 函数是将求得 "数组B中下标为 i的人" 在 "数组 A中的下标" // 下标 从 1 开始 // 例如 7, 在B中的下标为 1, 在A中下标为 7 // 例如 2, ............. 4, ........... 2 // alen 数组A 的长度 int BchangeintoA(int alen, int i, int blen) { i = blen+1 - i; int temp = i + (i-1)/(m-1); return temp; } const int N = 1000100; int F[N]; // F[N] 不为-1 则表示如果队列为N,唱歌的人的编号 // Example: F[2] = 1 , 如果序列长度为2,编号为1的 人唱歌 // 所有人下标从1开始 int Work(int num) { // 函数f 是去求解f[num],即队列中的人数 为num时唱歌的人的编号 if(F[num] != -1) { return F[num]; } if(num < m){ F[num] = 1; return 1; } Work(num-num/m); // 减而治之,例如如果是 7 个人的队列,在数一遍之后只剩下五个人 F[num] = BchangeintoA(num, F[num-num/m], num-num/m); // num >= m : 每一个F[num] 都是有 F[num-num/m] 推过来 // for(int i=1; i<=num; i++) { // cout << F[i] << " "; // } // cout << endl; return F[num]; } int main(){ int T; cin >> T; for(int t=1; t<=T; t++) { cin >> n >> m; memset(F, -1, sizeof(F)); // 对 f 进行初始化 int x; for(int i=1; i<=n; i++) { scanf("%d",&x); printf("%d\n",Work(x)); } } return 0; }
递推:
#include <iostream> #include <cstring> #include <cstdio> using namespace std; const int N = 1000100; int F[N]; int n, m; int BchangeintoA(int alen, int i, int blen) { i = blen+1 - i; int temp = i + (i-1)/(m-1); return temp; } int main(){ int T; cin >> T; for(int t=1; t<=T; t++) { cin >> n >> m; memset(F, -1, sizeof(F)); // 对 f 进行初始化 for(int i=1; i<m; i++) { F[i] = 1; } int x; int max = m-1; for(int i=1; i<=n; i++) { scanf("%d",&x); if(x <= max) { printf("%d\n",F[x]); } else { for(int i=max+1; i<=x; i++) { F[i] = BchangeintoA(i, F[i-i/m], i-i/m); } max = x; printf("%d\n",F[max]); } // for(int i=1; i<=max; i++){ // printf("%d ",F[i]); // } // printf("\n"); } } return 0; }