poj-2886

26 篇文章 0 订阅
13 篇文章 0 订阅
// 26584K   1407MS  G++
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;

#define MAX 500050

struct ChildeTreeNode {
    int childId;
    int childNum;
    int left;
    int right;
};

typedef struct ChildeTreeNode ChildeTreeNode;

ChildeTreeNode childTree[MAX<<2];

void buildChildTree(int left, int right, int pos) {
    childTree[pos].left = left;
    childTree[pos].right = right;
    childTree[pos].childNum = right - left + 1;
    childTree[pos].childId = -1;
    if (left == right) {
        childTree[pos].childId = left;
    } else {
        int mid = (left + right)>>1;
        buildChildTree(left, mid, pos<<1);
        buildChildTree(mid+1, right, pos<<1|1);
    }
}

int childId;
int childNum;
int beginChildSequence;

int getdividedNum(int N) {
    // printf("getdividedNum %d\n", N);
    int maxM = N/2;
    if (N == 1) {
        return 1;
    }
    int dividedNum = 2; // 1 and N
    for (int i = 2; i <= maxM; i++) {
        if (N%i == 0) {
            dividedNum++;
        }
    }
    return dividedNum;
} 

struct Child {
    int childId;
    char childName[12];
    int childInteger;
};

typedef struct Child Child; 

Child child[500010];

int jumpChildId;

void removeChild(int pos, int curSequence) {
    int rangeBegin = childTree[pos].left;
    int rangeEnd = childTree[pos].right;
    childTree[pos].childNum--;
    if (rangeBegin == rangeEnd) {
        jumpChildId = childTree[pos].childId;
        return; 
    } else {
        int leftChildNum = childTree[pos<<1].childNum;
        int rightChildNum = childTree[pos<<1|1].childNum;
        if (leftChildNum >= curSequence) {
            return removeChild(pos<<1, curSequence);
        } else {
            return removeChild(pos<<1|1, curSequence - leftChildNum);
        }
    }
}

int RPrime[]={//反素数  
    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
};  
  
int fact[]={//反素数约数个数  
    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
};

char childName[100];
int main() {
    while(scanf("%d %d", &childNum, &beginChildSequence) != EOF) {
        childId = 1;
        int childInteger;
        buildChildTree(1, childNum, 1);
        int luckChildJumpSequence = 0;

        int k = 0;
        int luckChildCandy;
        for (k = 0; k <sizeof(RPrime)/sizeof(int); k++) {
            if (childNum >= RPrime[k]) {
                luckChildJumpSequence = RPrime[k];
                luckChildCandy = fact[k];
            } else { // counter first > 
                break;
            }
        }

        for (int i = 1; i <= childNum; i++) {
            scanf("%s %d", child[i].childName, &child[i].childInteger);
            child[i].childId = i;
        }

        int maxDividedNum = 0;
        int maxChildId = 0;

        for (int i = 1; i <= childNum; i++) {
            removeChild(1, beginChildSequence);

            if (i == luckChildJumpSequence) {
                break;
            }

            if (childNum - i >0) {

                if (childNum - i == 1) {
                    beginChildSequence = 1;
                } else {
                    if (child[jumpChildId].childInteger < 0) {
                        beginChildSequence =
                            (beginChildSequence + child[jumpChildId].childInteger)%(childNum - i);
                        if (beginChildSequence == 0) {
                            beginChildSequence = childNum-i;
                        } if (beginChildSequence < 0) {
                            beginChildSequence += childNum - i;
                        }
                    } else { //child[i].childInteger >= 1
                        beginChildSequence =
                            (beginChildSequence + child[jumpChildId].childInteger-1)%(childNum - i);
                        if (beginChildSequence == 0) {
                            beginChildSequence = childNum-i;
                        }
                    }
                }
            }
        }
        printf("%s %d\n", child[jumpChildId].childName, luckChildCandy);
    }
}

线段树 + 反素数, 还算一道不错的综合题,经受过2828洗礼的比较容易发现思路,不过要达到时间要求,就必须还要用到反素数(这个如果数论题刷的多,也可以很容易看出来)。这道题也让我发现了自己基础数理上的缺乏。

反素数就是指一个正整数N,N对于比自己小的所有正整数,其分解的因子数量是最多的(就是题目要求的能够被整除的数 的 数目是最多的)。直接开了两个数组,一个记录反素数的大小,一个记录相应反素数的因子数量,有了这个以后,在有M个child的情况下,只需找到最大的不大于M的反素数L,第L个jump出去的child就是要求的。

那么现在问题就变为得到第L个junp出去的child是谁,这时候,就轮到线段树登场了,每个线段树的区间节点记录在改区间内,还有多少个child没有jump出去,一直细化到区间点,初始的线段树区间对应没有child跳出去的情况,然后每次根据跳出去child的位置和其card上的数字(注意利用求余)决定下一个要jump的child是新队列的第几个,然后用线段树逐级向下查找即可:

对于某个区间[l,r],查找第N个child, 如果 此区间还有X>=N个child, 那么递归的到左半区间[l,(l+r)>1], 否则 递归到右半区间 [(l+r)>1+1, r]查找第N-X个用户.

发现线段树对这种 不断变化的队列很好使.

这种还犯了些错误, 一开始开了脑洞,还搞了一个MAP来存child的信息,其实根本不需要,搞一个数组顺序保存就行了,然后就是求因子数量,不用反素数绝对TLE,  最后是求下一个jump的child的位置,也有些特殊的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值