ACM练级日志:POJ 2886 约瑟夫环,线段树和反素数
2014-08-09 22:32
281 查看
我昨天才知道模拟约瑟夫环是可以用线段树来解的……
不妨假设总共有N个人,他们的编号是1~N(这个编号很重要,影响到后面的推导)
怎么解呢?我们用一棵线段树,每个节点记录一下这个区间还剩下多少人,一开始当然就是R-L+1个人了。
然后我们要做的事情就是,每次先求出这次要出列的“绝对位置”,然后用线段树查一查这个绝对位置上站的是谁。
所谓“绝对位置”,就是1~N,不会变的,比如第一个出列的是2,那么绝对位置2那里站的应该是3,下次当你看到绝对位置2要出列的时候,就知道实际上要出列的人应该是3了。
以POJ 2886的样例为例:
有了这个概念之后,我们就可以用线段树求解“一个绝对位置上的人站的究竟是谁”的问题了。非常简单,如果我问x号上面站的是谁,那么我就看看左边是不是够x个,如果够x个,说明答案肯定在左子树里,否则就去右子树找x- node[p*2].count 即可。最后找到叶子节点就是所求的人。
找到以后删除之就是典型的单点删除操作,非常简单。
这道题还多多少少用到了一点反素数…… 反素数是说,一个数x是反素数,当且仅当所有比x小的数i,i的约数个数都小于x的约数个数。显然对于一个给定的n,最接近n的反素数就是我们要找的目标。比如n是5,那么我们就要找到第4个出队的人是谁。这样,由于50w以内的反素数十分有限,我们可以提前打表,然后知道n以后立刻找出目标反素数。当然暴力从复杂度上讲也没问题……
代码中还有一些细节问题,在注释里说吧。
不妨假设总共有N个人,他们的编号是1~N(这个编号很重要,影响到后面的推导)
怎么解呢?我们用一棵线段树,每个节点记录一下这个区间还剩下多少人,一开始当然就是R-L+1个人了。
然后我们要做的事情就是,每次先求出这次要出列的“绝对位置”,然后用线段树查一查这个绝对位置上站的是谁。
所谓“绝对位置”,就是1~N,不会变的,比如第一个出列的是2,那么绝对位置2那里站的应该是3,下次当你看到绝对位置2要出列的时候,就知道实际上要出列的人应该是3了。
以POJ 2886的样例为例:
绝对位置 | 1 | 2 | 3 | 4 |
实际站的人 | 1 | 2(+4) | 3 | 4 |
1 | 3(-1) | 4 | ||
1(out) | 4 | |||
4(最后留下) | ||||
找到以后删除之就是典型的单点删除操作,非常简单。
这道题还多多少少用到了一点反素数…… 反素数是说,一个数x是反素数,当且仅当所有比x小的数i,i的约数个数都小于x的约数个数。显然对于一个给定的n,最接近n的反素数就是我们要找的目标。比如n是5,那么我们就要找到第4个出队的人是谁。这样,由于50w以内的反素数十分有限,我们可以提前打表,然后知道n以后立刻找出目标反素数。当然暴力从复杂度上讲也没问题……
代码中还有一些细节问题,在注释里说吧。
#include<iostream> #include<stdio.h> #include<math.h> #include<string.h> #include<algorithm> #define D(x) cout<<#x<<" "<<x<<endl using namespace std; int n,k; struct ntype { int l,r; int count; } node[2000200]; struct stype { char namae[20]; int v; }; stype student[500010]; int max_candy = 0; const 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}; // 反素数 const 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}; // 反素数的约数个数 void del(int s, int e, int p, int tar) // 线段树单点删除tar号人 { node[p].count--; if(s==e) return; int mid = (s+e)/2; if(tar<=mid) del(s,mid, p*2, tar); else del(mid+1, e, p*2+1, tar); return; } int ask(int s, int e, int p, int q)// 询问q上站的是谁 { if(s==e) { return s; } int mid = (s+e)/2; if(node[p*2].count >= q) return ask(s, mid, p*2, q); else return ask(mid+1, e, p*2+1, q - node[p*2].count); } int prepare() // 准备好目标反素数和他的约数个数 { int ret_p = upper_bound(antiprime, antiprime + 36, n) - 1 - antiprime; //D(ret_p); int ret = antiprime[ret_p]; max_candy = factor[ret_p]; return ret; } void build(int s, int e, int p) { node[p].l=s; node[p].r=e; node[p].count= e-s+1; if(s==e) return; int mid = (s+e)/2; build(s,mid, p*2); build(mid+1, e, p*2+1); return; } void init() { memset(node,0,sizeof(node)); memset(student, 0, sizeof(student)); max_candy = 0; return; } int main() { while(scanf("%d %d", &n,&k)!=EOF) { init(); int i; for(i=1;i<=n;i++) { scanf("%s %d", student[i].namae, &student[i].v); } build(1,n,1); int end = prepare(); int now_absolute=k; int now_real; for(i=1;i<=end;i++) { //D(now_absolute); now_real = ask(1,n,1, now_absolute); //D(now_real); del(1,n,1,now_real); if(i==n) break; if(student[now_real].v > 0) { now_absolute = now_absolute - 1 -1 + student[now_real].v; // 第一个-1是因为我们从1开始标号,要先-1;第二个-1是因为我们要往右数,必须先退一格 now_absolute %= n-i; now_absolute = (now_absolute + (n-i) ) % (n-i); now_absolute +=1; // +1 是为了恢复成1~N } else { now_absolute = now_absolute - 1 + student[now_real].v; //第一个-1原因同上,由于我们是让右边的人靠过来,所以往左数不必再-1 now_absolute %= n-i; now_absolute = (now_absolute + (n-i) ) % (n-i); // 取模避免出现负数的方法:先模,然后加,再模 now_absolute += 1; } } printf("%s %d\n", student[now_real].namae, max_candy); //system("pause"); } return 0; }
相关文章推荐
- ACM练级日志:可持久化线段树初级-POJ 2104
- POJ 2886 Who Gets the Most Candies?(线段树 + 约瑟夫环 + 反素数)
- poj 2886 Who Gets the Most Candies?(线段树和反素数)
- POJ 2886 Who Gets the Most Candies? (线段树模拟约瑟夫环)
- poj 2886 Who Gets the Most Candies? 【线段树单点更新 + 反素数】
- POJ 2886 Who Gets the Most Candies?(线段树单点更新+反素数)
- poj 2886 线段树 (用反素数)
- poj 2886 Who Gets the Most Candies?(线段树、反素数)
- ACM练级日志:POJ 2318 叉积的简单应用
- POJ 2886 Who Gets the Most Candies?(线段树模拟约瑟夫环,高合成数)
- ACM练级日志: POJ 1389
- ACM练级日志:POJ 3074 数独与DLX
- ACM练级日志: POJ 1376
- POJ 2886 Who Gets the Most Candies? (高合成数&用线段树维护约瑟夫环)
- 线段树 + 反素数 poj2886
- ACM练级日志:POJ 3740 与Dancing Links
- POJ 2886 【线段树模拟约瑟夫环】.cpp
- POJ 2886 Who Gets the Most Candies? (线段树 约瑟夫环问题变种)
- [poj 2886] Who Gets the Most Candies[线段树][约瑟夫环][反素数]
- ACM练级日志:Level 3的线段树标记下传