poj2886 Who Gets the Most Candies?反素数+线段树

本文介绍了一个基于约瑟夫环问题的升级版算法实现,利用线段树和反素数概念来解决最大糖果数的问题。通过预处理反素数和因子数表,结合线段树维护剩余孩子的数量,实现高效求解。

分析:约瑟夫环的升级版,求最大糖果数(因子数可用反素数打表获得)

维护一颗线段树,区间存储剩余孩子数

这里涉及一个新的概念: 反素数


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 500000 + 10;
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
                };

int factor[]={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
             };


int seg[maxn<<2];
int name[maxn][11], value[maxn];

void build(int root, int l, int r){
    seg[root] = r-l+1; //初始化
    if (l == r) return;
    int m = (l+r) >> 1;
    build(root<<1, l, m);
    build(root<<1|1, m+1, r);
}

int update(int root, int l, int r, int key){
    seg[root] --;
    if (l == r) return l;
    int m = (l+r) >> 1;
    if (seg[root<<1] >= key) update(root<<1, l, m, key);
    else update(root<<1|1, m+1, r, key-seg[root<<1]); //类似排队问题
}

int main(){
    int n, k, &mod = seg[1];
    while (scanf("%d %d", &n, &k) != EOF){
        build(1, 1, n);
        for (int i = 1; i<=n; i++){
            scanf("%s%d", name[i], &value[i]);
        }
        int cnt = 0;
        while (antiprime[cnt] <= n && cnt < 35) cnt++;
        int Max = factor[cnt-1]; //根据反素数打表可求得最大因字数,可推得p值
        int p = antiprime[cnt-1];
        value[0] = 0;
        int pos = 0;
        for (int i = 0; i<p; i++){
            if (value[pos] > 0){ // 顺时针
                k=((k+value[pos]-2)%mod+mod)%mod+1;<span class="comment">//涉及模运算令代号从0开始,所以开始要减1,如同数空位线段树一样,k得到的是新树的第几个位置</span><span>  </span>
            }
            else{//逆时针
                k = ((k+value[pos]-1)%mod+mod)%mod+1;
            }
            pos = update(1, 1, n, k);
        }
        printf("%s %d\n", name[pos], factor[cnt-1]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值