跟poj2828的感觉很类似,就是加了很多附加的东西,诸如map或者是反素数,再譬如,这是个环而不是一行东西。
http://blog.youkuaiyun.com/julyana_lin/article/details/7880173
题意:http://poj.org/problem?id=2886
约瑟夫问题。第i个出去的孩子得到f(i),f(i)表示i的因数个数。从第k个孩子开始,问谁得到最多的糖果。
解:
先找小于等于n的因数最多的p,即小于等于n的最大反素数。(对于任何正整数x,起约数的个数记做g(x).例如g(1)=1,g(6)=4.
如果某个正整数x满足:对于任意i(0<i<x),都有g(i)<g(x),则称x为反素数.)
然后呢,搞个线段树。线段树节点表示这个区间还有多少人。用线段树求解是为了算出下一个人在整个数组(所有人)中的绝对编号(坐标)。
假设当前出圈的是剩余孩子中的第K个,他手中的数字为A。若A大于零,下一个出圈的就应该是去除第K个孩子剩余孩子中的第(K-1+A-1)%n+1个(本来应该是(K - 1 + A) % n个,为了避免这个数刚好等于n的情况,将1加在外面);若A小于零,下一个出圈的就应该是剩余孩子中的第((K-1+A)%n+n)%n+1个(本来是((K+A)%n+n)%n)。
原先不懂的地方:
怎么在第k个位置之后找到第a个非空的数?---------------求相对坐标。用相对坐标去找出绝对坐标。
/*
Pro: 0
Sol:
date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define lson l,m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define havem int m = (l + r ) >> 1
#define maxn 500100
using namespace std;
const int antiprime[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,
1260,1680,2520,5040,7560,10080,15120,20160,25200,27720,
45360,50400,55440,83160,110880,166320,221760,277200,
332640,498960,554400,665280};
const int factorNum[]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,64,72,80,84,
90,96,100,108,120,128,144,160,168,180,192,200,216,224};
char str[maxn][15];
int val[maxn],f[maxn],n,k,sum[maxn << 2];
void build(int l, int r, int rt){
sum[rt] = (r - l + 1);
if (l == r) return;
havem;
build(lson); build(rson);
}
int getpos(int pos,int l, int r, int rt){
sum[rt] --;
if(l == r) return l;
havem;
if(sum[ls] >= pos) return getpos(pos,lson);
else return getpos(pos - sum[ls],rson);
}
int main(){
while(~scanf("%d%d",&n,&k)){
for(int i = 1; i <= n; i ++){
scanf("%s%d",str[i],&val[i]);
}
build(1,n,1);
int rpos = 0,s,cnt = 0;
while(cnt < 35 && antiprime[cnt] <= n) cnt ++;
s = antiprime[cnt - 1];
int &mod = sum[1]; val[0] = 0;//mod永远是剩下的孩子的个数
for(int i = 1; i <= s; i ++){
if(val[rpos] > 0)//k为相对位置,这个是解题很重要的一步!!!
k = ((k + val[rpos] - 2) % mod + mod ) % mod + 1;//
else
k = ((k + val[rpos] - 1 ) % mod + mod ) % mod + 1;
rpos = getpos(k,1,n,1);
}
printf("%s %d\n",str[rpos],factorNum[cnt - 1]);
}
return 0;
}
求反素数的方法: Max表示的是分解因数的最大个数,pos表示那个分解因数最大个数的那个数。
int Max = 0, pos;
for(int i = 1; i < n; i ++)
for(int j = i; j < n; j += i){
f[j] ++;
if(f[j] > Max){
Max = f[j];
pos = j;
}
}