POJ 2886 线段树对约瑟夫问题的模拟
2012-07-23 22:22
239 查看
这个问题其实就是对约瑟夫问题的模拟,单纯的暴力模拟肯定是会超时的,用线段树来优化,只要算出当前要走的人在当前的数组中是第几个人就可以了
刚开始的时候没怎么弄明白,后来看了人家的分析之后才茅塞顿开
如果当前走的是第k个人,他报的数是m,那么下一个要走的人就是 k-1+m(当然还得取模)。为什么是这样呢,k走了之后,其实k之后的人相当于都向前挪了一个位置,所以需要减一个1. 在取完模以后,算得的结构就是下次要走的人是当前队伍中的第几个人,然后通过线段树去查找一下就好,线段树的节点记录其对应区间内的人数和,所以,查找时只需:
如果查找的第x个人不超过左子树的节点值,即左子树中有那么多的人,此时往左子树查找;否则,往右子树查找,但是要注意,必须将查找的人数减去左子树的人数
找到人之后应该对该节点(人最后肯定是一个节点)及其所有父节点的值更新,节点的值减1,然后push_up一下就好了,返回的值就是该人在最初队列中的坐标
下面还有一个问题,怎么使得他的约数个数最多呢??网上有很多解法都是反素数,在这里我用了筛法,把1-500000的每个数的约数个数给算出来了,具体怎么筛的通过代码很容易看出来,筛完之后,在ans数组里记录的是ans[i]表示有i个人时最多能得到的糖的数量,index[i]记录当有i个人时,第index[i]个跳出的孩子能得到最多的糖
以下就是代码:
刚开始的时候没怎么弄明白,后来看了人家的分析之后才茅塞顿开
如果当前走的是第k个人,他报的数是m,那么下一个要走的人就是 k-1+m(当然还得取模)。为什么是这样呢,k走了之后,其实k之后的人相当于都向前挪了一个位置,所以需要减一个1. 在取完模以后,算得的结构就是下次要走的人是当前队伍中的第几个人,然后通过线段树去查找一下就好,线段树的节点记录其对应区间内的人数和,所以,查找时只需:
如果查找的第x个人不超过左子树的节点值,即左子树中有那么多的人,此时往左子树查找;否则,往右子树查找,但是要注意,必须将查找的人数减去左子树的人数
找到人之后应该对该节点(人最后肯定是一个节点)及其所有父节点的值更新,节点的值减1,然后push_up一下就好了,返回的值就是该人在最初队列中的坐标
下面还有一个问题,怎么使得他的约数个数最多呢??网上有很多解法都是反素数,在这里我用了筛法,把1-500000的每个数的约数个数给算出来了,具体怎么筛的通过代码很容易看出来,筛完之后,在ans数组里记录的是ans[i]表示有i个人时最多能得到的糖的数量,index[i]记录当有i个人时,第index[i]个跳出的孩子能得到最多的糖
以下就是代码:
#include <cstdio> #include <cstring> #define FF(i,a,b) for(int i=a; i<b; i++) const int MAX = 500010; int sum[MAX<<2]; template <class T> T max(T a,T b) { return (a>b)?a:b; } void push_up(int rt) { sum[rt] = sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) { if(l==r) { sum[rt] = 1; return; } int mid = r+l>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); push_up(rt); } int update(int p,int l,int r,int rt) { if(l==r) { sum[rt] = 0; return l; } int mid = r+l>>1; int res; if(p<=sum[rt<<1]) res = update(p,l,mid,rt<<1); else res = update(p-sum[rt<<1],mid+1,r,rt<<1|1); push_up(rt); return res; } struct point { char s[15]; int x; }ch[MAX]; int f[MAX]; int ans[MAX]; int index[MAX]; int main() { int n,k; memset(f,0,sizeof(f)); //下面的双重循环就是筛法求约数个数,f里存的是除1和本身外的约数个数 for(int i=2; i*2<MAX; i++) for(int j=i*2; j<MAX; j+=i) f[j]++; ans[1] = 1; index[1] = 1; for(int i=2; i<MAX; i++) if(ans[i-1]>=f[i]+2) { ans[i] = ans[i-1]; index[i] = index[i-1]; } else { ans[i] = f[i]+2; index[i] = i; } while(scanf("%d%d",&n,&k)==2) { build(1,n,1); for(int i=1; i<=n; i++) scanf("%s%d",ch[i].s,&ch[i].x); int x = index ; int tt; for(int i=0; i<x; i++) { tt = update(k,1,n,1); if(i==x-1) break; if(ch[tt].x>0) k = (k+ch[tt].x-2)%sum[1]+1; //计算下一次的跳出的人在现在这个队列中排第几 //注意是从1开始记得,所以最后有个+1,括号里是-2,自己算一下就能体会 else k = ((k+ch[tt].x-1)%sum[1]+sum[1])%sum[1]+1; //与上面原理一样 } printf("%s %d\n",ch[tt].s,ans ); } return 0; }
相关文章推荐
- poj -- 2886 约瑟夫问题(线段树)
- 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(线段树模拟约瑟夫环)
- (POJ2886)Who Gets the Most Candies? <线段树模拟约瑟夫问题,找第k个人>
- POJ 2886 【线段树模拟约瑟夫环】.cpp
- POJ 2886 Who Gets the Most Candies?(线段树、模拟)
- POJ 2886 Who Gets the Most Candies? (线段树模拟约瑟夫环)
- POJ 3750 小孩报数问题 (线性表思想 约瑟夫问题 数组模拟运算的 没用循环链表,控制好下标的指向就很容易了)
- POJ2886-Who Gets the Most Candies?-模拟约瑟夫环+反素数表(线段树上二分+爆搜打表)
- poj 2886 Who Gets the Most Candies? 线段树动态求第k大的数
- poj 2777 Count Color(线段树+染色问题)
- poj 1012 约瑟夫问题
- poj 3750 小孩报数问题(模拟)
- poj1021 约瑟夫问题 利用数学公式
- 线段树 约瑟夫问题 codevs 1282
- POJ-2777 Count Color(线段树,区间染色问题)
- poj 2886 Who Gets the Most Candies?(数据结构:线段树+DFS反素数打表)
- POJ 2886 Who Gets the Most Candies? (打表+线段树)