转载自:http://blog.csdn.net/sdj222555/article/details/6878651
反素数拓展参照:http://blog.csdn.net/ACdreamers/article/details/25049767
题目大意就是一群熊孩子做游戏,第一个出队的人是编号为k的人。此后出队的人就是按照前一个人手里的编号。如果是正数+m就是这个人的左边的第m个人。如果是负数-m,就是 这个人的右边第m个人。由于这个人出队了。对下一个人有影响,所以+m的时候,是k+m-1。-m的时候是k+m。因为是他后边的人所以没有影响。因为可能出现负数和0的情况,所以就有下面的取模时的处理。线段树的每个节点保存的是这个区间还有多少个位置。所以每次更新时,如果左孩子节点的空位够了,搜索左孩子,否则搜索右孩子。可以建树参照对样例模拟一下。
还不懂怎么打反素数表。所以是cooy的。
附代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 using namespace std; 5 6 #define lson l, m, rt<<1 7 #define rson m+1, r, rt<<1|1 8 #define N 500010 9 10 int tree[N<<2]; 11 12 const int antiprime[] = { // 反素数表 13 1,2,4,6,12,24,36,48,60,120,180,240,360,720,840, 14 1260,1680,2520,5040,7560,10080,15120,20160,25200, 15 27720,45360,50400,55440,83160,110880,166320,221760, 16 277200,332640,498960,554400,665280 17 }; 18 19 const int factorNum[] = { // 对应的约数个数 20 1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60, 21 64,72,80,84,90,96,100,108,120,128,144,160,168,180, 22 192,200,216,224 23 }; 24 25 struct child{ // 保存输入时的节点信息 26 char name[15]; 27 int val; 28 }c[N]; 29 30 void Build(int l, int r, int rt) { // 建树 31 tree[rt] = r-l+1; 32 if (l == r) 33 return; 34 int m = (l+r)>>1; 35 Build(lson); 36 Build(rson); 37 } 38 39 int Update(int p, int l, int r, int rt) { // 第p个人出去。Update函数可以理解。 40 tree[rt]--; 41 if (l == r) 42 return r; 43 int m = (l+r)>>1; 44 if (p<=tree[rt<<1]) 45 return Update(p, lson); 46 else return Update(p-tree[rt<<1], rson); 47 } 48 49 int main() { 50 int i, n, &mod = tree[1]; 51 // mod 保存的就是一共有多少个人、 52 int k; 53 54 while(~scanf("%d%d", &n, &k)) { 55 // 输入建树 56 for (i=1; i<=n; ++i) { 57 scanf("%s%d", c[i].name, &c[i].val); 58 } 59 Build(1, n, 1); 60 61 // 小于等于n的最大的反素数。 62 int cnt = 0; 63 while(cnt < 35 && antiprime[cnt] <= n) { 64 cnt++; 65 } 66 cnt--; 67 // 先找到1-n范围内的约束个数最大的数。 68 69 // pos是记录当前位置该出队的人的ID 70 int pos = 0; 71 c[pos].val = 0; 72 73 // 找antiprime[cnt]出队的人的名字、 74 for (i=0; i<antiprime[cnt]; ++i) { // 循环的次数就是直到这个人出队。 75 76 // 这两个if是根据 这个人手里牌的编号来推算下一个出队列的人的当前位置、 77 if (c[pos].val > 0) 78 k = ((k+c[pos].val-2)%mod+mod)%mod+1; 79 else k=((k+c[pos].val-1)%mod+mod)%mod+1; 80 81 // pos 记录的是当前循环出队的人的所在位置、 82 pos = Update(k, 1, n, 1); 83 cout << pos << "====\n"; 84 } 85 printf("%s %d\n", c[pos].name, factorNum[cnt]); 86 } 87 return 0; 88 }