poj 2886 反素数+约瑟夫环线段树单点更新

题意:

有n(5*10^5)只小朋友做游戏,每个人手上有一张卡片,上面记录着一个非零的数val。

现在他们按照1~n坐成一个环。

然后游戏从第k只小朋友开始,他先跳出这个圈,然后如果他手上的val为正,则下一个小朋友的位置就是他当前位置+val;

相反的,如果他手上的val为负,则下一个小朋友的位置就是他当前位置-val。

现在,第p次的小朋友跳出圈圈的时候他会获得f(p)个糖果,f(p)的定义是p的因子数。

然后问,哪个小朋友出圈时能够得到最多的糖果,输出他的名字,并且输出糖果数。


解析:

无从下手啊。。。

所以从f(p)处下手,f(p)咋一看是确定的值,也就是说,可以确定是第几个小朋友出圈获得最多的糖果。

这个f(p)就是反素数,而他获得的糖果数就是当前这个反素数对应的因子个数。

关于反素数看上一篇。

这样就好办了,因为确定了p,所以只要模拟整个过程,然后确定第p次出来的小朋友是在原来的哪个位置就好了。

用线段树来维护这个游戏,线段树里保存的是当前的小朋友的个数,每当跳出一个小朋友,就更新当前的树,并返回当前小朋友原来的位置lo,就行了。

蛋疼的地方是做还模的时候会模出0,所以模拟的时候。。。。

附带讨论区里的测试数据。


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <climits>
#include <cassert>
#define LL long long
#define lson lo, mi, rt << 1
#define rson mi + 1, hi, rt << 1 | 1

using namespace std;
const int maxn = 500000 + 10;
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double ee = exp(1.0);

int antiprime[]= {0,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};
int factorNum[]= {0,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};


struct People
{
    char name[20];
    int val;
} people[maxn];

int sum[maxn << 2];

void pushup(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void build(int lo, int hi, int rt)
{
    if (lo == hi)
    {
        sum[rt] = 1;
        return ;
    }
    int mi = (lo + hi) >> 1;
    build(lson);
    build(rson);
    pushup(rt);
}

int update(int k, int lo, int hi, int rt)
{
    sum[rt]--;
    if (lo == hi)
        return lo;
    int mi = (lo + hi) >> 1;
    if (k <= sum[rt << 1])
        return update(k, lson);
    else
        return update(k - sum[rt << 1], rson);
}

int main()
{
#ifdef LOCAL
    freopen("in.txt", "r", stdin);
#endif // LOCAL
    int n, k;
    while (~scanf("%d%d", &n, &k))
    {
        for (int i = 1; i <= n; i++)
        {
            scanf("%s%d", people[i].name, &people[i].val);
        }
        ///
        int t = 1;
        while (antiprime[t] <= n)
            t++;
        t--;
        int p = antiprime[t];//第p个小朋友所得到的factorNum是最大的。
        ///
        build(1, n, 1);
        int pos = 0;
        //模拟p次就好了
        while (p--)
        {
            if (0 < people[pos].val)
                k = (k - 1 + people[pos].val - 1) % sum[1] + 1;
            else if (people[pos].val < 0)
                k = ((k - 1 + people[pos].val) % sum[1] + sum[1]) % sum[1] + 1;
            pos = update(k, 1, n, 1);
        }
        printf("%s %d\n", people[pos].name, factorNum[t]);
    }
    return 0;
}
///input
/*
2 1
5705 -24464
16827 23281
2 1
292 -153
18716 -17421
2 1
17035 26299
23811 -28703
4 1
32662 27644
12859 20037
27529 9741
3035 -12316
2 1
15890 -23805
15350 24370
6 3
4966 -11840
26308 -13931
24626 -32439
21538 5537
22929 -2082
31115 4833
2 1
26924 28745
5829 -6270
6 1
23655 24767
12052 -4031
16941 -1150
3430 -13966
18007 30191
12287 15457
2 1
27506 -24946
29168 -16413
2 1
27595 -6483
24350 3602
2 1
8281 -24484
1999 -53
2 1
17807 -22483
6617 14310
5 1
22798 31556
11008 6224
14989 -32609
20485 -3195
30523 14343
4 1
15281 -14798
28009 20798
23622 20472
6038 -12292
2 1
2634 16202
20328 -20055
2 1
31998 -21881
10021 -18651
3 1
26869 -21003
13401 14688
16423 15255
2 1
4169 -30932
17189 -25721
6 1
30145 9512
3753 -21718
16279 12423
12529 -16687
19866 17437
23195 193
2 1
31316 -11701
12263 5786
2 1
28321 27756
27982 -23646
5 3
12044 10466
26439 26292
26154 20024
20649 -4745
4474 -8313
3 1
4414 26477
29334 -25824
20159 -24372
2 1
17192 2668
8480 3102
5 1
13061 29972
27432 31003
22725 27593
142 8492
13064 -31286
2 1
25760 19711
7285 -4667
2 1
26302 -22658
4678 22466
3 1
1926 31060
32170 -4757
30227 9576
6 1
2625 14474
31928 5629
6902 28520
24596 -123
10195 13261
8260 1264
2 1
29790 21520
21763 -30188
*/
///output
/*

16827 2
18716 2
23811 2
12859 3
15350 2
21538 4
5829 2
3430 4
29168 2
24350 2
1999 2
6617 2
14989 3
6038 3
20328 2
10021 2
16423 2
17189 2
19866 4
12263 2
27982 2
4474 3
29334 2
8480 2
142 3
7285 2
4678 2
30227 2
24596 4
21763 2
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值