题意:一个线段上的约瑟夫问题。
做法:简单暴力,思路清晰的线段树,或者DP
线段树做法:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<iostream>
#include<complex>
#include<string>
#include<set>
#include<map>
#include<algorithm>
//#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define nn 3000200
#define mm 400200
//#define ll long long
#define ULL unsiged long long
#define pb push_back
#define mod 1000000007
#define inf 0x3fffffff
#define eps 0.00000001
int n, m, q;
int ans[nn];
int t[nn << 2];
void build(int l, int r, int rt)
{
if (l == r)
{
t[rt] = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
t[rt] = t[rt << 1] + t[rt << 1 | 1];
}
int find(int l, int r, int rt, int x)
{
if (l == r) return l;
int mid = (l + r) >> 1;
int k = t[rt << 1];
//printf("%d %d %d\n", l, r, k);
if (x <= k) return find(l, mid, rt << 1, x);
else return find(mid + 1, r, rt << 1 | 1, x - k);
}
void pushup(int rt)
{
t[rt] = t[rt << 1] + t[rt << 1 | 1];
}
void updata(int l, int r, int rt, int x)
{
if (l == r) {
t[rt] = 0;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) updata(l, mid, rt << 1, x);
else updata(mid + 1, r, rt << 1 | 1, x);
pushup(rt);
}
int main()
{
int tt;
scanf("%d", &tt);
while (tt--)
{
scanf("%d%d%d", &n, &m, &q);
build(1, n, 1);
int cnt = 1;
//printf("root=%d\n", t[1]);
while (t[1])
{
int k = 1;
int x = find(1, n, 1, k);
ans[cnt++] = x;
//printf("x=%d\n", x);
updata(1, n, 1, x);
while (k+(m-1) <= t[1])
{
k += (m - 1);
x = find(1, n, 1, k);
ans[cnt++] = x;
//printf("x=%d\n", x);
updata(1, n, 1, x);
}
}
//for (int i = 1; i < cnt; i++) printf("%d ", ans[i]);
while (q--)
{
int x;
scanf("%d", &x);
printf("%d\n", ans[x]);
}
}
return 0;
}
DP版代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<iostream>
#include<complex>
#include<string>
#include<set>
#include<map>
#include<algorithm>
//#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:1024000000,1024000000")
#define nn 3000200
#define mm 400200
//#define ll long long
#define ULL unsiged long long
#define pb push_back
#define mod 1000000007
#define inf 0x3fffffff
#define eps 0.00000001
int n, m, q;
int ans[nn];
int add[nn];//第i轮开始的坐标
int dp[nn];//i点在第几轮被杀
int num[nn];//i点再该轮第几个被杀
void init()
{
int tmp = n;
int cnt = 0;
add[0] = 0;
while (tmp)
{
cnt++;
add[cnt] = add[cnt - 1] + (tmp - 1) / m + 1;
tmp -= (tmp - 1) / m + 1;
}
for (int i = 0; i < n; i++)
{
if (i%m == 0)
{
dp[i] = 0;
num[i] = i / m + 1;
}
else
{
dp[i] = dp[i - i / m - 1] + 1;
num[i] = num[i - i / m - 1];
}
}
for (int i = 0; i < n; i++)
ans[add[dp[i]] + num[i]] = i;
}
int main()
{
int tt;
scanf("%d", &tt);
while (tt--)
{
scanf("%d%d%d", &n, &m, &q);
init();
while (q--)
{
int x;
scanf("%d", &x);
printf("%d\n", ans[x]+1);
}
}
return 0;
}