// 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的位置,也有些特殊的。