http://poj.org/problem?id=2886
题意:N 个小孩围成一圈,他们被顺时针编号为 1 到 N。每个小孩手中有一个卡片,上面有一个非 0 的数字,游戏从第 K 个小孩开始,他告诉其他小孩他卡片上的数字并离开这个圈,他卡片上的数字 A 表明了下一个离开的小孩,如果 A 是大于 0 的,则下个离开的是左手边第 A 个,如果是小于 0 的,则是右手边的第 -A 个小孩。游戏将直到所有小孩都离开,在游戏中,第 p 个离开的小孩将得到 F(p) 个糖果,F(p) 是 p 的约数的个数,问谁将得到最多的糖果。输出最幸运的小孩的名字和他可以得到的糖果。
思路:我觉得这题难就难在如何在一开始把线段树和约瑟夫环联系起来,就是这点困扰了我许久。题中模型是一个环,下一人的退出序号是由前异人卡片上的数字决定的,这里有两个难点。第一,普通约瑟夫环求的是最后一人出场序号,这个是求约数最多的出场序号,必须先打素数表来确定递归终止条件;第二普通约瑟夫环每个人之间的递推间隔是固定的,而这个递推间隔由卡片决定,而要想知道卡片必须知道那个人的实际位置。而我们知道约瑟夫环在递推过程中下一步即为上一步的相对位置,而这个相对位置正好是从0开始数的第几位,这样不是和poj2828的排队问题一样了吗?根据优先级大小选择更新位置,只不过上一题是进队,所以逆序,这一题出队就是正序啊!这样就通过相对位置找到了实际位置,也就和卡片数字有了联系。接着就是递推,这里的递推式有点复杂,只不过多了一次+n)%n而已,本质是不变的,顺时针和逆时针的区别我们直接把负数加上去就好。再有,约瑟夫环的处理必须是0~n-1,所以刚开始k需减一。还有顺时针遍历时,看别人说是因为他自己本身要走,所以减一,而逆时针就不需要了(看懂了吗?反正我是没懂。。。泣不成声)。
这题要注意的点简直不要太多,我一口老血差点吐出来。。
#include <stdio.h>
#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
const int N = 500010;
const int INF = 1e8;
struct line
{
int l, r;
int len;
}tree[4*N];
int num[N], maxnum, maxid;
void prime(int n)
{
memset(num, 0, sizeof(num));
for(int i = 1; i <= n; i++)
{
num[i]++;
for(int j = i*2; j <= n; j+=i)
{
num[j]++;
}
}
maxnum = num[1];;
maxid = 1;
for(int i = 2; i <= n; i ++)
{
if(maxnum < num[i])
{
maxnum = num[i];
maxid = i;
}
}
}
void build(int i, int l, int r)
{
tree[i].l = l;
tree[i].r = r;
tree[i].len = r-l+1;
if(l == r)
{
return;
}
int mid = (l + r) >> 1;
build(i*2, l, mid);
build(i*2+1, mid+1, r);
}
int query(int i, int pos) //由相对位置找到实际位置
{
tree[i].len--;
if(tree[i].l == tree[i].r)
{
return tree[i].l;
}
if(pos <= tree[i*2].len) query(i*2, pos);//注意这里是<=因为线段从1开始
else query(i*2+1, pos-tree[i*2].len);
}
char name[N][15];//这两个不能定义在main里面,不然无法编译
int val[N];
int main()
{
// freopen("in.txt", "r", stdin);
int n, k;
while(~scanf("%d%d", &n, &k))
{
prime(n);
memset(val, 0, sizeof(val));
for(int i = 1; i <= n; i++)
{
scanf("%s%d", name[i], &val[i]);
}
build(1, 1, n);
int pos;
for(int i = 0; i < maxid; i++)
{
pos = query(1, k);
n--;
if(n == 0) break;
if(val[pos] > 0) k = ((k-1+val[pos]-1)%n+n)%n+1;
else k = ((k-1+val[pos])%n+n)%n+1;
}
printf("%s %d\n", name[pos], maxnum);
}
return 0;
}