题意:n个孩子做约瑟夫的游戏,第p个离开的孩子可以得到F[p]个糖果,问谁得到的糖果最多。其中F[p],表示p的因子的个数。
题解:用线段树模拟约瑟夫环。不过用树状数组+二分应该也可以吧。另外貌似可以用“反素数”来剪枝,不过我不会,所以只能预处理下F[p]。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAX 500009
#define L(u) (u<<1)
#define R(u) (u<<1|1)
struct NODE
{
int l, r, rest;
};
struct Item
{
char name[11];
int num;
};
NODE node[MAX*3];
Item player[MAX];
int pri[MAX], a[MAX], pn;
int F[MAX], id[MAX];
//F[i]表示数i的因子个数。
//id[i]表示在小于i的所有数中,因子最多的数的下标。相当于剪枝了。
void prime ()
{
pn = 0;
memset(a,0,sizeof(a));
int i, j;
for ( i = 2; i < MAX; i++ )
{
if ( a[i] == 0 )
pri[pn++] = i;
for ( j = 0; j < pn && i * pri[j] < MAX && (pri[j] <= a[i] || a[i] == 0); j++ )
a[i*pri[j]] = pri[j];
}
}
void cal_F ()
{
F[1] = 1;
int n, m, i, cnt;
for ( i = 2; i < MAX; i++ )
{
if ( a[i] == 0 )
{
F[i] = 2; continue;
}
m = n = i;
F[i] = 1;
while ( a[n] )
{
cnt = 0;
while ( m % a[n] == 0 )
{
m = m / a[n];
cnt++;
}
F[i] *= ( cnt + 1 );
n = m;
}
if ( n != 1 ) F[i] *= 2;
}
}
void cal_maxF_id ()
{
id[1] = 1;
for ( int i = 2; i < MAX; i++ )
{
id[i] = id[i-1];
if ( F[i] > F[id[i-1]] )
id[i] = i;
}
}
void build ( int u, int l, int r )
{
node[u].l = l;
node[u].r = r;
node[u].rest = r - l + 1;
if ( l == r ) return;
int mid = ( l + r ) / 2;
build ( L(u), l, mid );
build ( R(u), mid+1, r );
}
int query ( int u, int k )
{
node[u].rest--;
if ( node[u].l == node[u].r )
return node[u].l;
if ( k <= node[L(u)].rest )
return query ( L(u), k );
return query ( R(u), k-node[L(u)].rest );
}
int main()
{
prime();
cal_F();
cal_maxF_id();
int n, k;
while ( scanf("%d%d",&n,&k) != EOF )
{
int i, p, lnum, rnum, res, mmax;
for ( i = 1; i <= n; i++ )
scanf("%s %d",player[i].name, &player[i].num);
build ( 1, 1, n );
k = k % n;
if ( k == 0 ) k = n;
p = query ( 1, k );
mmax = F[1]; res = p;
lnum = k - 1, rnum = n - k;
for ( i = 2; i <= id[n]; i++ )
{
//模拟约瑟夫环关键是求出K
if ( player[p].num > 0 )
{
player[p].num %= ( lnum + rnum );
if ( player[p].num == 0 ) player[p].num = lnum + rnum;
if ( rnum >= player[p].num )
k = lnum + player[p].num;
else
k = player[p].num - rnum;
}
else
{
player[p].num = -player[p].num;
player[p].num %= ( lnum + rnum );
if ( player[p].num == 0 ) player[p].num = lnum + rnum;
if ( lnum >= player[p].num )
k = lnum - player[p].num + 1;
else
k = lnum + rnum - (player[p].num-lnum) + 1;
}
p = query ( 1, k ); //k表示从1开始数的第k个数。
lnum = k - 1; rnum = ( n - i + 1 ) - k;
if ( F[i] > mmax ) { res = p; mmax = F[i]; }
}
printf("%s %d\n",player[res].name,mmax);
}
return 0;
}