Death Sequence
Time Limit: 16000/8000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 295 Accepted Submission(s): 123
Problem Description
You may heard of the Joseph Problem, the story comes from a Jewish historian living in 1st century. He and his 40 comrade soldiers were trapped in a cave, the exit of which was blocked by Romans. They chose suicide over capture and decided that they would form a circle and start killing themselves using a step of three. Josephus states that by luck or maybe by the hand of God, he and another man remained the last and gave up to the Romans.
Now the problem is much easier: we have N men stand in a line and labeled from 1 to N, for each round, we choose the first man, the k+1-th one, the 2*k+1-th one and so on, until the end of the line. These poor guys will be kicked out of the line and we will execute them immediately (may be head chop, or just shoot them, whatever), and then we start the next round with the remaining guys. The little difference between the Romans and us is, in our version of story, NO ONE SURVIVES. Your goal is to find out the death sequence of the man.
For example, we have N = 7 prisoners, and we decided to kill every k=2 people in the line. At the beginning, the line looks like this:
1 2 3 4 5 6 7
after the first round, 1 3 5 7 will be executed, we have
2 4 6
and then, we will kill 2 6 in the second round. At last 4 will be executed. So, you need to output 1 3 5 7 2 6 4. Easy, right?
But the output maybe too large, we will give you Q queries, each one contains a number m, you need to tell me the m-th number in the death sequence.
Now the problem is much easier: we have N men stand in a line and labeled from 1 to N, for each round, we choose the first man, the k+1-th one, the 2*k+1-th one and so on, until the end of the line. These poor guys will be kicked out of the line and we will execute them immediately (may be head chop, or just shoot them, whatever), and then we start the next round with the remaining guys. The little difference between the Romans and us is, in our version of story, NO ONE SURVIVES. Your goal is to find out the death sequence of the man.
For example, we have N = 7 prisoners, and we decided to kill every k=2 people in the line. At the beginning, the line looks like this:
1 2 3 4 5 6 7
after the first round, 1 3 5 7 will be executed, we have
2 4 6
and then, we will kill 2 6 in the second round. At last 4 will be executed. So, you need to output 1 3 5 7 2 6 4. Easy, right?
But the output maybe too large, we will give you Q queries, each one contains a number m, you need to tell me the m-th number in the death sequence.
Input
Multiple cases. The first line contains a number T, means the number of test case. For every case, there will be three integers N (1<=N<=3000000), K(1<=K), and Q(1<=Q<=1000000), which indicate the number of prisoners, the step length of killing, and the number of query. Next Q lines, each line contains one number m(1<=m<=n).
Output
For each query m, output the m-th number in the death sequence.
Sample Input
1 7 2 7 1 2 3 4 5 6 7
Sample Output
1 3 5 7 2 6 4
Author
BUPT
Source
Recommend
题目大意:
有N个人站成一列,每次我们杀死第一个人并且每隔K个人杀死一个,直到队伍末端。有Q个查询,每次输入一个数x,输出第x个死的人。(原来约瑟夫问题这么黑暗。。。)
解题思路:
我们先只考虑第一次杀人和第二次杀人的情况。我们令下标从0开始,那么对于第i个人,如果i%K==0那么他一定是第一次死。否则他在第一次杀人后的位置是i-i/K-1,也就是他比i-i/K-1位置的人多活一回合,而第i/i-K-1位置的人的存活回合数也可以用刚才的方法求出。那么我们就可以得到状态转移方程:
dp[i]=i%K?dp[i-i/K-1]+1:0;其中I表示位置为i的人,dp[i]储存第i个人存活的回合数。
所以我们用O(1)的时间,从0扫到N-1就可以求出每个人的存活回合数,对于每个回合的人,我们只要在开一个数组计数就可以得到他是这个回合的第几个人以及这个回合一共死多少人。这样我们就可以算出每个人是第几个死的。不过由于题目查询要求输入第几个死,输出位置,所以我们还需要再用O(1)的时间扫一遍,将关系倒过来。
总时间复杂度:预处理O(N),查询O(1)
这题的内存卡STL,开始用vector转换就无限MLE。。。
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <map>
using namespace std;
#define fi first
#define se second
const int maxn=3000000+5;
int N,K,Q;
pair<int,int> dp[maxn];//第几个回合死,这一回合第几个死(下标从0开始)
int cnt[maxn];//统计每回合的死亡人数
int ans[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(cnt,0,sizeof cnt);
scanf("%d%d%d",&N,&K,&Q);
for(int i=0;i<N;++i)//dp
{
dp[i].fi=i%K?dp[i-i/K-1].fi+1:0;
dp[i].se=cnt[dp[i].fi]++;
}
for(int i=1;i<N;++i)//处理出前i回合死亡人数
{
if(!cnt[i])
break;
cnt[i]+=cnt[i-1];
}
for(int i=0;i<N;++i)//关系转换
ans[(dp[i].fi?cnt[dp[i].fi-1]:0)+dp[i].se]=i;
while(Q--)
{
int x;
scanf("%d",&x);
printf("%d\n",ans[x-1]+1);
}
}
return 0;
}