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

点击打开链接

<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[N];
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[N];
int fprim[N],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[N][20],d[N];
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[N];
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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值