poj2886线段树+反素数

8<---------------------------------------------------

题意:

N个小朋友围一圈。

指定一个人为起始点之后,此人出圈。

按照这个人手中卡片的数字找到下一个人,下一个人出圈。

。。。

直到最后一个人出圈。


8<--------------------------------------------

思路:

一:当期指定的人在第 K 个位置,他出圈后,找到下一个人在剩下的人中的位置 next_k。

	if(A >= 0) next_k = (k-1+A-1)%n + 1;
        else next_k = ((k-1+A)%n + n)%n + 1;

二:利用线段树树状数组求出接下来出圈的人原来的所在的位置。

以此可以求得第 P 个除圈的人。

三:题目描述最后一句说 F( x )为 x 的约数的个数。求这一组数的最大的 F(x),输出x, 以及相应的人名。

此处需要反素数表。

利用反素数相关知识,求出小于 N 的最大反素数 F(X), 得到X。

利用思路二可以求出第F(X)个出圈的人。

输出人名和x。


8<-------------------------------------------

刚开始不知道用到反素数,只暴力球了各个数的因子个数,无数个WA 和 超时, 错误百出。

搜了一下题解,用的反素数打表。

第一次接触反素数,了解了一下发现维基 和 百度说法不一。

不过总算知道是什么了。


啊, 第一次纯手写线段树不用看模板,脑海中的线段树模板有区间查询,这里是根据一个特定的值查询,稍微改了一下就好使了。

(呃。。。。我费了好长时间写这个查询啊……甚至还写了二级查询……慢慢调才搞出来简短的这个……当然代码里看不出来啦……弱弱仍需努力……)


8<---------------------------------------------


/*poj2886线段树*/
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;

#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
#define maxn 500040

char str[maxn][10];
int A[maxn];
int sum[maxn<<2];
int n, k;

int operate(int a, int b){
    return a+b;
}

void PushUp(int rt){
    sum[rt] = operate(sum[rt<<1], sum[rt<<1|1]);
}

void build(int l, int r, int rt){
    if(l == r){
        sum[rt] = 1;
        return ;
    }
    int m = (l+r)>>1;
    build(lson);
    build(rson);
    PushUp(rt);
}

void Update(int p, int add, int l, int r, int rt){
    if(l == r){
        sum[rt] = add;
        return ;
    }
    int m = (l+r)>>1;
    if(p <= m) Update(p, add, lson);
    else if(p>m) Update(p, add, rson);
    PushUp(rt);
}

int Query(int k, int l, int r, int rt){
    if(l==r && k == 1 && sum[rt] == k){
        return l;
    }

    int m = (l+r)>>1;
    int ret = 0;
    if(sum[rt<<1] < k){
        ret =  Query(k-sum[rt<<1], rson);
    }else if(sum[rt<<1] >= k){
        ret = Query(k, lson);
    }

    return ret;
}

int main(){
    //freopen("in.txt", "r", stdin);
    int maxp_pos, maxp;

    int a[37]={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,500001};
    int b[37]={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,1314521};

    while(scanf("%d %d", &n, &k) != EOF){
        memset(sum, 0, sizeof(sum));
        for(int i=1; i<=n; i++){
            scanf("%s %d", str[i], &A[i]);
        }
        build(1, n, 1);

        int i=0;
        while(a[i] <= n)  i++;
        int p = a[i-1];
        maxp = b[i-1];

        int cnt = 1, next_k = k, tmp = k;
        int tmp_n = n;

        while(cnt<n){
            cnt++;
            Update(tmp, 0, 1, n, 1);
            tmp_n --;

            if(A[tmp]>=0) next_k = (next_k-1+A[tmp]-1)%tmp_n+1;
            else next_k = ((next_k-1+A[tmp])%tmp_n+tmp_n)%tmp_n + 1;
            tmp = Query(next_k, 1, n, 1);

            if(cnt == p) maxp_pos = tmp;
        }
        printf("%s %d\n", str[maxp_pos], maxp);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值