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

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 ≤ KN) 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值