您的位置:首页 > 其它

POJ 2886Who Gets the Most Candies? 线段树+反素数求因子个数最多

2016-08-08 13:55 302 查看
点击打开链接

<span style="font-size:18px;">题意:n个熊孩子按顺时针排列,每个人受伤都有一张牌,牌上有一个数字,从第k个孩子开始出队,出队的熊孩子卡上数字是K,则顺时针第k人是下一个出队的,负数则逆时针,第P个出队的会得到的糖果数是p的因子个数,输出得到最多糖果的人和他的糖果数,如果有多个,则输出最先出队的。</span>
<span style="font-size:18px;">
</span>
<span style="font-size:18px;">思路:用线段树来存储整个区间中的剩余人的个数、然后用反素数来找因子数最多的人的出队的序号。</span>


反素数就是 对 x来说约束个数 G(x),如果 对于 i<x 有 G(i)<G(x)

则称x为反素数。

对于 n 个孩子 ,最后拿到最多的糖果就是 小于等于 n 的最大 反素数。但是 是哪个孩子并不知道,那么就要进行模拟

对于 k 位置的 孩子,他的 数字是 +num 那么因为他自己本身是要被踢走的,所以相对位置 为k= k+num-1

如果数字是 -num,那么按正着数就没影响,k=k-num。线段树存储当前区间共有多少个人,每一次找到第k (前面有k-1个)个孩子,经过的区间都要 -1,然后记录被踢走的孩子编号

///反素数打表

void Solve(){
memset(ans,0,sizeof(ans));
for(int i=1;i<=n;i++){
ans[i]++;
for(int j=2*i;j<=n;j+=i)
ans[j]++;
}
int max=ans[1];
id=1;
for(int i=2;i<=n;i++)   //找出第几个人跳出获得的糖最多
if(ans[i]>max){
max=ans[i];
id=i;
}
}


#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#define LL long long
#define INF 0x3f3f3f3f
#define lson L,mid,ind<<1
#define rson mid+1,R,ind<<1|1
using namespace std;
const int N=2010000;
int sum
;
void pushup(int ind){
sum[ind]=sum[ind<<1]+sum[ind<<1|1];
}
void build(int L,int R,int ind){
sum[ind]=R-L+1;
if(L==R){
return ;
}
int mid=L+(R-L)/2;
build(lson);
build(rson);
}
int update(int d,int L,int R,int ind){
sum[ind]--;
if(L==R){
return L;
}
int mid=L+(R-L)/2;
if(d<=sum[ind<<1]) return update(d,lson);
else return update(d-sum[ind<<1],rson);
}
bool vis
;
int fprim
,num;
void init(){
num=0;
memset(fprim,0,sizeof(fprim));
for(int i=1;i<=500001;i++){
fprim[i]++;
for(int j=i*2;j<=500001;j+=i){
fprim[j]++;
}
}
}
int a
[20],d
;
int main(){
init();
int n,k;
while(~scanf("%d%d",&n,&k)){
build(1,n,1);
int Max=1;
int id=1;
for(int i=1;i<=n;i++){
if(fprim[i]>Max){Max=fprim[i];
id = i;
}
}
// cout<<id<<" "<<Max<<endl;
for(int i=1;i<=n;i++){
scanf("%s%d",a[i],&d[i]);
}
int huan=n;
d[0]=0;
int pos=0;
for(int i=0;i<id;i++){
if(d[pos]>0){
k=((k-1+d[pos]-1)%huan+huan)%huan+1;
}
else k=((k-1+d[pos])%huan+huan)%huan+1;
pos=update(k,1,n,1);
huan--;
}
// cout<<pos<<endl;
printf("%s %d\n",a[pos],Max);
}
return 0;
}

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define LL long long
#define bug puts("***********");
#define lson L,mid,ind<<1
#define rson mid+1,R,ind<<1|1
using namespace std;
const int N=500010;
char s[20];
int d;
int tree[N*4],n,k;
///反素数表示 因子个数逐渐递增的(最小的数 )
const int antiprime[]={1,2,4,6,12,24,36,48,60,120,180,240,360,720,840,
1260,1680,2520,5040,7560,10080,15120,20160,25200,
27720,45360,50400,55440,83160,110880,166320,221760,
277200,332640,498960,554400,665280
};
///因子个数 与上面的数是一一对应的。
const int factorNum[]={1,2,3,4,6,8,9,10,12,16,18,20,24,30,32,36,40,48,60,
64,72,80,84,90,96,100,108,120,128,144,160,168,180,
192,200,216,224
};

void Build(int L,int R,int ind){
tree[ind]=R-L+1;
if(L==R){
return ;
}
int mid=(L+R)/2;
Build(lson);
Build(rson);
}
int Update(int val,int L,int R,int ind){
tree[ind]--;
if(L==R){
return R;
}
int mid=(L+R)/2;
if(val<=tree[ind<<1]) return Update(val,lson);
else return Update(val-tree[ind<<1],rson);
}

struct node{
char s[20];
int d;
}A
;
int main(){
while(~scanf("%d%d",&n,&k)){
Build(1,n,1);
for(int i=1;i<=n;i++){
scanf("%s%d",A[i].s,&A[i].d);
}
int cnt=0;

///找因子最多的数的下标
while(antiprime[cnt]<=n)cnt++;  ///以后自加自减写在外面,写在里面会出错
cnt--;
int pos=0;
int cna=n;
A[0].d=0;

///只要是出队的序列数<=antiprime[cnt]就是因子数最多的(就算是并列关系,此时的数满足最小)

///此处下标为0 表示第一出队的序号。
for(int i=0;i<antiprime[cnt];i++){       ///自己用坐标推推。找规律,
if(A[pos].d>0){
k=((k-1+A[pos].d-1)%cna+cna)%cna+1;
}
else{
k=((k-1+A[pos].d)%cna+cna)%cna+1;
}
pos=Update(k,1,n,1);
cna--;                              ///人数在减少
}

printf("%s %d\n",A[pos].s,factorNum[cnt]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: