Description
n个人站成一排,每次从左边第一个人开始每隔k个人让一个人出列,q此查询,每次查询某次出列人的编号
Input
第一行一整数T表示用例组数,每组用例首先输入三个整数n,k,q,之后q行每行一个整数m表示查询第m个出列的人的编号(1<=n<=3000000,1<=q<=1000000,1<=m<=n)
Output
对于每次查询,输出第m个出列的人的编号
Sample Input
1
7 2 7
1
2
3
4
5
6
7
Sample Output
1
3
5
7
2
6
4
Solution
一轮过后和原来问题比只是人的编号发生变化,故可以转化为子问题求解,不妨设这n个人的编号是0~n-1,对于第i个人,如果i%k=0,那么这个人一定是第一轮出列的第i/k+1个人;如果i%k!=0,那么这个人下一轮的编号就是i-i/k-1,令s[i]表示编号为i的人是第几轮出列的,t[i]表示编号为i的人是其s[i]轮第几个出列的,那么有s[i]=i%k?s[i-i/k-1]+1:1,t[i]=i%k?t[i-i/k-1]:i/k+1,之后对s序列求一遍前缀和得到sum序列,那么编号为i的人就是第sum[s[i]-1]+t[i]个出列的
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 3333333
int T,n,k,q,s[maxn],t[maxn],sum[maxn],ans[maxn];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&k,&q);
memset(sum,0,sizeof(sum));
int tot=1;
for(int i=0;i<n;i++)
{
if(i%k==0)
s[i]=1,t[i]=i/k+1,sum[1]++;
else
{
int j=i-i/k-1;
s[i]=s[j]+1,t[i]=t[j],sum[s[i]]++,tot=max(tot,s[i]);
}
}
for(int i=1;i<=tot;i++)sum[i]+=sum[i-1];
for(int i=0;i<n;i++)ans[sum[s[i]-1]+t[i]]=i+1;
while(q--)
{
int temp;
scanf("%d",&temp);
printf("%d\n",ans[temp]);
}
}
return 0;
}

本文介绍了一种算法问题——隔k出列问题,并详细解释了如何通过转化子问题来解决该问题。通过一轮过后观察人员编号的变化规律,利用s[i]和t[i]数组记录每个人出列的轮次及位置,最终实现高效查询指定出列人员的原始位置。
396

被折叠的 条评论
为什么被折叠?



