題目鏈接:http://poj.org/problem?id=2886
這道題的難點不在於線段樹,而在於反素數和下一個人位置的確定。反素數我準備用一個博客寫。這裏先不講。知道了反素數和下一個人的位置,這道題就轉變爲求第k大數。我記得求第K大數有很多題了, 我的博客就寫了4-5題了。由於存在刪除所以要採用線段樹或樹狀數組。先寫樹狀數組,再寫線段樹,今天又把他們求第k數的思想想了一下。
樹狀數組版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int n, k;
const int MAX = 500000 + 10;
int c[MAX];
struct node
{
char name[20];
int card;
}child[MAX];
int a[37]={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,500001};
int b[37]={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,1314521};
int lowbit(int x)
{
return x&(-x);
}
void update(int x, int v)
{
while (x<=n)
{
c[x] += v;
x +=lowbit(x);
}
}
int getsum(int x)
{
int sum = 0;
while (x)
{
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int find(int x)
{
int l = 1, r = n, mid;
while (l < r)
{
mid = (l +r)>>1;
if (getsum(mid)<x)
{
l = mid + 1;
}
else
{
r = mid;
}
}
return l;
}
int main()
{
int i, j;
while (scanf("%d %d", &n, &k) == 2)
{
memset(c, 0, sizeof(c));
for (i=1; i<=n; i++)
{
update(i, 1);
scanf("%s%d", child[i].name, &child[i].card);
}
i=0;
while (a[i]<=n)
{
i++;
}
i--;
int pos = 0, t = n;
for (j=0; j<a[i]; j++)
{
t--;
pos = find(k);
if (j==a[i]-1)
{
break;
}
update(pos, -1);
if (child[pos].card>0)
{
k = (k + child[pos].card-1) % t;
if (k ==0)
{
k = t;
}
}
else
{
k = ((k + child[pos].card)%t+t) % t;
if (k == 0)
{
k = t;
}
}
}
printf("%s %d\n", child[pos].name, b[i]);
}
}
線段樹版本:
#include <stdio.h>
#include <string.h>
const int MAX = 500000 + 10;
char name[MAX][11];
int card[MAX];
int c[37]={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,500001};
int b[37]={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,1314521};
struct node
{
int l;
int r;
int sum;
}a[MAX<<2];
void pushup(int pos)
{
a[pos].sum = a[pos<<1].sum + a[pos<<1|1].sum;
}
void build(int l, int r, int pos)
{
a[pos].l = l;
a[pos].r = r;
if (l == r)
{
a[pos].sum = 1;
return;
}
int mid = (l + r) >> 1;
build(l, mid, pos<<1);
build(mid+1, r, pos<<1|1);
pushup(pos);
}
int query(int key, int pos)
{
a[pos].sum--;
if (a[pos].l == a[pos].r)
{
return a[pos].r;
}
if (key<=a[pos<<1].sum)
{
return query(key, pos<<1);
}
else
{
return query(key - a[pos<<1].sum, pos<<1|1);
}
}
int main()
{
int n, k, i, ansscore, anspos, pos;
while (scanf("%d%d", &n, &k) == 2)
{
i = 0;
while (c[i] <= n)
{
i++;
}
anspos = c[i-1];
ansscore = b[i-1];
build(1, n, 1);
for (i=1; i<=n; i++)
{
scanf("%s%d", name[i], &card[i]);
}
for (i=1; i<=anspos; i++)
{
n--;
pos = query(k, 1);
if (i == anspos)
{
break;
}
if (card[pos]>0)
{
k = (k + card[pos]-1) % n;
if (k ==0)
{
k = n;
}
}
else
{
k = (k + card[pos]%n+n) % n;
if (k == 0)
{
k = n;
}
}
}
printf("%s %d\n", name[pos], ansscore);
}
}