lightoj1097 lucky number 报数类问题 线段树的单点更新
来源:互联网 发布:类似maka软件 编辑:程序博客网 时间:2024/05/25 23:56
题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1097
题目:
Lucky numbers are defined by a variation of the well-known sieve of Eratosthenes. Beginning with the natural numbers strike out all even ones, leaving the odd numbers 1, 3, 5, 7, 9, 11, 13, ... The second number is 3, next strike out every third number, leaving1, 3, 7, 9, 13, ... The third number is 7, next strike out every seventh number and continue this process infinite number of times. The numbers surviving are called lucky numbers. The first few lucky numbers are:
1, 3, 7, 9, 13, 15, 21, 25, 31, 33, ...
In this problem your task is to find the nth lucky number where n is given in input.
Input
Input starts with an integer T (≤ 10000), denoting the number of test cases.
Each case contains an integer n (1 ≤ n ≤ 105).
Output
For each case, print the case number and the nth lucky number.
Sample Input
Output for Sample Input
2
2
100000
Case 1: 3
Case 2: 1429431
分析:这个题给我一种约瑟夫环的问题的感觉,但人家约瑟夫是一个环,算了我们姑且叫它报数问题。这种报数问题感觉就是一个在剩余序列中的相对位置和在原序列中绝对位置的关系。我们用sum数组存储在某个区间上剩余元素的个数,我们发现线段树这一数据结构可以很好的解决这一类问题。
上面这道题中,我们发现实质上每次需要报的数就是从剩余序列中相对位置为2,3,... ...的数对应都在原序列中绝对位置的数。算了好长一句话还是看代码吧!唔,不过这代码还不完善,因为边界没处理好,如果大佬有解决方法恳求指点。
code:
#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int MAXN=2e6;
int sum[(MAXN+5)<<2];int ans[MAXN],visit[MAXN];
//sum是用来维持某区间上剩余数的个数,叶子节点也是区间,只不过左右区间重合了
void PushUp(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
sum[rt]=l&1;//因为原始的序列没有偶数,只有奇数
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
PushUp(rt);
}
//通过在剩余序列中的相对位置p,去原序列中找到绝对位置l,并删除它
int update(int p,int l,int r,int rt){
if(l==r){
sum[rt]=0;
return l;
}
int pos;
int m=(l+r)>>1;
if(sum[rt<<1]>=p)pos=update(p,lson);
else pos=update(p-sum[rt<<1],rson);
PushUp(rt);
return pos;
}
//功能与上面的update类似,只不过只询问,不删除
int Query(int p,int l,int r,int rt){
if(l==r){
return l;
}
int pos;
int m=(l+r)>>1;
if(sum[rt<<1]>=p)pos=Query(p,lson);
else pos=Query(p-sum[rt<<1],rson);
return pos;
}
void init(){//打表,初始化
build(1,MAXN,1);
memset(visit,0,sizeof(visit));
memset(ans,0,sizeof(ans));
for(int i=2;i<=MAXN;++i){//i为每次报的数在剩余序列中的相对位置
if(i>sum[1])return;//现在根本没那么多人辣!根节点表示剩余序列中还有多少人!
int k=Query(i,1,MAXN,1);//查询相对位置i的绝对位置
int ID=k;//需要删除的人的编号
for(;;){
int pos=update(ID,1,MAXN,1);
visit[pos]=1;//visit数组记录这个人已经被删除了,方便后来打表
if(pos+ID>MAXN)break;//每次单向循环的终止条件没处理好。
ID=ID+k-1;//删除一个人后第ID个人,第n(n>ID)个人就变成了第n-1个人
}
}
}
int main(void){
//打表
init();
for(int i=1,j=1;i<=MAXN;++i){
if((i&1)&&!visit[i]){
ans[j++]=i;
}
}
int T;scanf("%d",&T);
int CASE=0;
while(T--){
int k;scanf("%d",&k);
printf("Case %d: %d\n",++CASE,ans[k]);
}
}
- lightoj1097 lucky number 报数类问题 线段树的单点更新
- poj 3750 小孩报数问题(线段树-单点更新)
- HDU1394 Minimum Inversion Number [暴力] [线段树-单点更新]
- Minimum Inversion Number----HDU_1394----线段树之单点更新
- hdu 1754 Minimum Inversion Number 线段树 单点更新
- hdu 1394 Minimum Inversion Number(线段树 单点更新)
- HDU1394:Minimum Inversion Number(线段树单点更新)
- hdu1394 Minimum Inversion Number 线段树,单点更新
- HDU2852_KiKi's K-Number(线段树/单点更新)
- hdu1394 Minimum Inversion Number(线段树单点更新||暴力)
- HDU1394-Minimum Inversion Number(线段树单点更新)
- Minimum Inversion Number(线段树单点更新+逆序数)
- hdu 1394 Minimum Inversion Number(线段树单点更新)
- hdu 1394 Minimum Inversion Number(线段树-单点更新)
- HDU2852 KiKi's K-Number 线段树单点更新
- 数据结构 线段树 hdu1394 Minimum Inversion Number(单点更新)
- HDU 1394 Minimum Inversion Number [线段树->单点更新]【数据结构】
- Minimum Inversion Number (单点更新 线段树 )
- servlet编码
- Sublime text追踪函数插件:ctags
- install opencv with linux
- 基于hessian协议调用java方法-一个map例子
- 写在最前面
- lightoj1097 lucky number 报数类问题 线段树的单点更新
- android 网络编程--socket tcp/ip udp http之间的关系
- 推送的设计
- T1外部计数输入
- 一个可以免费下载牛逼论文的网站Arxiv
- 26:滑雪
- MATLAB im2double、double、mat2gray、im2uint8和uint8的区别及使用
- 我的第一篇博客终于诞生了
- spring mvc