[POJ 2886] Who Gets the Most Candies? (Joseph环问题 + 树状数组)
2016-04-30 22:11
211 查看
POJ - 2886
有 N个人顺时针围成一圈,刚开始第 K个人退出圈,他手上有个数字 A_k如果 A_k为正,则他左数 A_k个人退出,如果 A_k为负,则他右数 A_k个人退出
同样,接下来退出的那个人手上也有个数字,一直进行下去直到圈内所有人都离开
第 p个人退出的时候,他能得到 F(p)个糖果,F(p) 为 p约数的个数
问得到最多糖果的最早退出的人是谁,他得到了几个糖果
这是个很经典的问题,叫做 Josep环问题,以前我只会模拟求
虽然现在也只会模拟求但利用树状数组,可以使得这个过程变得很高效
维护一个树状数组,每个点存的是每个人的现在的序号
当第 i个人退出时,其后所有人向前移一位,序号减一 ( bit.add(i,-1) )
然后总人数再减一,并计算下一个人在新的环中的序号
下一次在树状数组中二分查找这个序号的下界,然后循环
用样例来说明,现在有四个人:1 2 3 4
第一次第二个人退出后,第二位以后减一:1 1 2 3
然后下一个人的序号为 (1+4)%3=2
二分查找这个人在树状数组中的位置,即为第 3个
然后重复这个过程:1 1 1 2
关于题目提到的要求的答案,其实就是不超过 N的最大的反素数
所以其实可以先把反素数 M求出来,然后进行到第 M次就行了,不必模拟到底
由于树状数组从 1开始标号,所以取模的时候有诸多不便
再加上每个人的偏移量会有负数,所以存在一些坑点
1) 当偏移量为负数时,要加上 1
2) 取模会有负数,要 +tot然后再 %tot
3) 如果模的结果为 0,要手动变为 tot
#include <cstdio> #include <iostream> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <map> #include <set> #include <queue> using namespace std; typedef pair<int,int> Pii; typedef long long LL; typedef unsigned long long ULL; typedef double DBL; typedef long double LDBL; #define MST(a,b) memset(a,b,sizeof(a)) #define CLR(a) MST(a,0) #define Pow2(a) (a*a) const int maxn=5e5+10; const int tprm[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; struct BIT { int *Bit;int siz; BIT(int size):siz(size){Bit=new int[size+1];memset(Bit,0,sizeof(int)*(size+1));} ~BIT(){delete []Bit;} int lowbit(int x){return x&-x;} void add(int x, int v) { while(x<=siz) { Bit[x]+=v; x+=lowbit(x); } } int sum(int x) { int res=0; while(x>0) // Indexed start from 1 { res+=Bit[x]; x-=lowbit(x); } return res; } void update(int x,int v){add(x,v);add(x+1,-v);} void update(int l,int r,int v){add(l,v);update(r+1,-v);} int query(int x){return sum(x);} }; int N,K; char name[maxn][15]; int nxt[maxn]; LL aprm,aprmc; void get_aprm(int,int,LL,LL,LL); //anti_prime int main() { bool first=1; while(~scanf("%d%d", &N, &K)) { BIT bit(N); for(int i=1; i<=N; i++) { scanf(" %s %d", name[i], &nxt[i]); bit.update(i,i); } aprm=1e18,aprmc=0; get_aprm(0,63,1,1,N); int ans=aprmc,M=aprm,p=K,tot=N,l=K,r; for(int i=1; i<M; i++) { tot--; bit.add(l,-1); if(nxt[l]<0) nxt[l]+=1; p=((bit.query(l)+nxt[l])%tot+tot)%tot; if(p==0) p=tot; l=1,r=N; while(l<r) { int mid=(l+r)>>1; if(bit.query(mid)>=p) r=mid; else l=mid+1; } } printf("%s %d\n", name[l], ans); } return 0; } void get_aprm(int np,int top,LL fcnt,LL now,LL LimN) { if(fcnt>aprmc){aprm=now;aprmc=fcnt;} if(fcnt==aprmc&&now<aprm){aprm=now;} if(np>=16) return; for(int i=1; i<=top; i++) { if(now>LimN/tprm[np]) break; now*=tprm[np]; get_aprm(np+1,i,fcnt*(i+1),now,LimN); } }
相关文章推荐
- Codeforces 665A - Buses Between Cities
- HTML5培训第10节课堂笔记(盒子模型、行内与块级、float、定位、html5布局)
- Linq 语法举例
- JAVA为什么要配置环境变量,怎样配置
- 《Nodejs开发加密货币》之三:Nodejs让您的前端开发像子弹飞一样
- VS---“重新生成解决方案”和"生成解决方案"的学习
- 设计模式学习笔记——解释器模式
- iOS备忘录之本地数据详解
- Redis几个认识误区
- 别废话,上车
- Scrapy学习日记1
- S3C24x0 kernel 源码分析
- 事务性质和隔离级别
- Qt学习笔记之如何保存软件关闭前的相关设置
- 数据结构与逻辑代码(一)
- Redis和Memcache对比及选择
- linux内核分析综合总结
- 343. Integer Break
- iOS中实现获取文本内容的宽高
- 2.一个奇怪的fork程序