您的位置:首页 > 其它

POJ_2886_Who Gets The Most Candies_线段树+反素数

2014-09-22 19:09 330 查看
66666666

题意:

一群熊孩子按照顺时针编号,然后面向圆心坐着cosplay约瑟夫,每个小孩有一个不为0的数字,其绝对值小于等于10^8,从某一个小孩开始,他被整出圈子,如果他的数字x是正数,那他左边第x个小孩就是下一个出去的,如果他的数字是负数,那他右边第-x个小孩就是下一个出去的,游戏结束直到所有的熊孩子都被整出去了。第i个被整出去的熊孩子能拿到f(i)个糖果,f(i)是可以整除i的正整数个数。求哪个熊孩子拿到的糖最多,是多少。

Input
There are several test cases in the input. Each test case starts with two integers
N (0 < N ≤ 500,000) and K (1 ≤
K ≤ N) on the first line. The next N lines contains the names of the children (consisting of at most 10 letters) and the integers (non-zero with magnitudes within 108) on their cards in increasing order of the children’s
numbers, a name and an integer separated by a single space in a line with no leading or trailing spaces.
Output

Output one line for each test case containing the name of the luckiest child and the number of candies he/she gets. If ties occur, always choose the child who jumps out of the circle first.

这个问题难解决的是熊孩子出去以后他们的编号要变,当然用链表可以很好解决,但是一听说有糖10^5个熊孩子来做游戏,链表就没法破了,必须用O(logn)的算法解决查找下一个熊孩子的问题。于是就可以用线段树查找编号为i的熊孩子现在排在第几位,通过线段树记录区间内熊孩子的个数。模拟的过程调了好久。

另一个麻烦的是f(i)。网上大多数人是利用了反素数的概念。根据定义,一个数的约数个数小于任何一个小于他的 数字的约数个数,则它是一个反素数,500000内的反素数数量不多,可以打表解决。

但是看到了芳姐的博客里面的解法,用了埃氏筛法和dp。具体做法是先筛出所有的质数,然后循环所有数找他们约数个数。f(1)=1,i为质数f(i)=2,合数只需要跑个朴素质数测试找到它的一个质因数a,然后求出该质因数的次方b,dp[i]=dp[i/(a^b)]*(b+1)即可。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
#define mxn 600000
char name[mxn][20];
int num[mxn];
int ll[mxn<<2],rr[mxn<<2],sum[mxn<<2],indx[mxn<<2];
int n,k;
void build(int l,int r,int id){
ll[id]=l,rr[id]=r;
if(l==r){
sum[id]=1;
indx[id]=l;
return;
}
int m=(l+r)>>1,ls=id<<1,rs=ls|1;
build(l,m,ls);
build(m+1,r,rs);
sum[id]=sum[ls]+sum[rs];
indx[id]=-1;
}
void update(int ind,int id){
if(ll[id]==rr[id]){
indx[id]=-1;
sum[id]=0;
return;
}
int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
if(ind<=m)	update(ind,ls);
else	update(ind,rs);
sum[id]=sum[ls]+sum[rs];
}
int nxt(int ind,int id){
if(ll[id]==rr[id])	return 0;
int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
if(ind>m)	return sum[ls]+nxt(ind,rs);
else	return nxt(ind,ls);
}
int find(int nm,int id){
if(ll[id]==rr[id])	return indx[id];
int m=(ll[id]+rr[id])>>1,ls=id<<1,rs=ls|1;
if(nm<sum[ls])	return find(nm,ls);
else	return find(nm-sum[ls],rs);
}
bool is_prime[mxn];
int candy[mxn];
void init(){
memset(is_prime,true,sizeof(is_prime));
memset(candy,0,sizeof(candy));
for(int i=2;i<mxn;++i)	if(is_prime[i])
for(int j=i*2;j<mxn;j+=i)
is_prime[j]=false;
candy[1]=1;
for(int i=2;i<mxn;++i){
if(is_prime[i])	candy[i]=2;
else	for(int j=2;j<i;++j)	if(i%j==0){
int tem=i,cnt=0;
while(tem%j==0){
++cnt;
tem/=j;
}
candy[i]=candy[tem]*(cnt+1);
break;
}
}
}
int main(){
init();
while(scanf("%d%d",&n,&k)!=EOF){
for(int i=0;i<n;++i)
scanf("%s%d",name[i],&num[i]);
build(0,n-1,1);
int cnt=n,ans=-10,tem,rec;
--k;
while(true){
--cnt;
update(k,1);
if(ans<candy[n-cnt]){
ans=candy[n-cnt];
rec=k;
}
if(!cnt)	break;
tem=nxt(k,1);
tem+=num[k];
if(num[k]>0)	--tem;
while(tem<0)	tem+=(1+(-tem)/cnt)*cnt;
k=tem%cnt;
k=find(k,1);
}
printf("%s %d\n",name[rec],ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: