由於模擬約瑟夫環比較費時,可以利用線段樹的查詢功能來優化。
如下圖,把一個環拆開變成一條直線,就可以查詢某段區間有多少個空位置了。
反素數經典博文:打開連接
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <string.h>
#include <map>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
int maxn[500001 << 2], v[500005];
char ch[500005][10], cc[500005][10];
int a[100] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53}, ans, temp, n, k;
void dfs(int deep, int limit, int tmp, int num)
{
if(tmp > n) return ;
if(num > ans)
{
ans = num;
temp = tmp;
}
else if(num == ans && tmp < temp)
{
temp = tmp;
}
for(int i = 1; i <= limit; i++)
{
if(tmp > n / a[deep]) break;
dfs(deep + 1, i, tmp * a[deep], num * (i + 1));
tmp *= a[deep];
}
}
void build(int l, int r, int rt)
{
if(l == r)
{
maxn[rt] = 1;
return ;
}
int mid = (l + r) >> 1;
build(l, mid, lson);
build(mid + 1, r, rson);
maxn[rt] = maxn[lson] + maxn[rson];
}
int query(int l, int r, int rt, int L, int R)
{
if(L <= l && R >= r)
{
return maxn[rt];
}
int mid = (l + r) >> 1;
int ans = 0;
if(mid >= L) ans = query(l, mid, lson, L, R);
if(mid < R) ans += query(mid + 1, r, rson, L, R);
return ans;
}
int update(int l, int r, int rt, int where)
{
if(l == r)
{
maxn[rt] = 0;
return l;
}
int mid = (l + r) >> 1;
int ans = 0;
if(maxn[lson] >= where) ans = update(l, mid, lson, where);
else ans = update(mid + 1, r, rson, where - maxn[lson]);
--maxn[rt];
return ans;
}
int main()
{
while(~scanf("%d %d", &n, &k))
{
dfs(0, 60, 1, 1);
for(int i = 1; i <= n; i++)
{
scanf("%s %d", ch[i], &v[i]);
}
int tmp = k;
build(1, n, 1);
strcpy(cc[1], ch[tmp]);
update(1, n, 1, tmp);
for(int i = 2; i <= n; i++)
{
if(v[tmp] > 0)
v[tmp] = (v[tmp] - 1) % maxn[1] + 1;
else
v[tmp] = maxn[1] + 1 - (-v[tmp] - 1) % maxn[1] - 1;
int k = query(1, n, 1, tmp + 1, n);
if(v[tmp] <= k) tmp = update(1, n, 1, maxn[1] - k + v[tmp]);
else tmp = update(1, n, 1, maxn[1] - k + 1 - (maxn[1] + 1 - v[tmp]));
strcpy(cc[i], ch[tmp]);
}
printf("%s %d\n", cc[temp], ans);
}
return 0;
}