poj 2886 线段树

跟poj2828的感觉很类似,就是加了很多附加的东西,诸如map或者是反素数,再譬如,这是个环而不是一行东西。

http://blog.youkuaiyun.com/julyana_lin/article/details/7880173

题意:http://poj.org/problem?id=2886

       约瑟夫问题。第i个出去的孩子得到f(i),f(i)表示i的因数个数。从第k个孩子开始,问谁得到最多的糖果。

解:

      先找小于等于n的因数最多的p,即小于等于n的最大反素数。(对于任何正整数x,起约数的个数记做g(x).例如g(1)=1,g(6)=4.

如果某个正整数x满足:对于任意i(0<i<x),都有g(i)<g(x),则称x为反素数.)

      然后呢,搞个线段树。线段树节点表示这个区间还有多少人。用线段树求解是为了算出下一个人在整个数组(所有人)中的绝对编号(坐标)。

      假设当前出圈的是剩余孩子中的第K个,他手中的数字为A。若A大于零,下一个出圈的就应该是去除第K个孩子剩余孩子中的第(K-1+A-1)%n+1个(本来应该是(K - 1 + A) % n个,为了避免这个数刚好等于n的情况,将1加在外面);若A小于零,下一个出圈的就应该是剩余孩子中的第((K-1+A)%n+n)%n+1个(本来是((K+A)%n+n)%n)。

原先不懂的地方:

     怎么在第k个位置之后找到第a个非空的数?---------------求相对坐标。用相对坐标去找出绝对坐标。

/*
Pro: 0

Sol:

date:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <set>
#include <map>
#include <vector>
#define lson l,m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define ls rt << 1
#define rs rt << 1 | 1
#define havem int m = (l + r ) >> 1
#define maxn 500100
using namespace std;

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};

char str[maxn][15];
int val[maxn],f[maxn],n,k,sum[maxn << 2];
void build(int l, int r, int rt){
    sum[rt] = (r - l + 1);
    if (l == r) return;
    havem;
    build(lson);    build(rson);
}
int getpos(int pos,int l, int r, int rt){
    sum[rt] --;
    if(l == r) return l;
    havem;
    if(sum[ls] >= pos) return getpos(pos,lson);
    else return getpos(pos - sum[ls],rson);
}
int main(){
    while(~scanf("%d%d",&n,&k)){
        for(int i = 1; i <= n; i ++){
            scanf("%s%d",str[i],&val[i]);
        }
        build(1,n,1);
        int rpos = 0,s,cnt = 0;
        while(cnt < 35 && antiprime[cnt] <= n) cnt ++;
        s = antiprime[cnt - 1];
        int &mod = sum[1];  val[0] = 0;//mod永远是剩下的孩子的个数
        for(int i = 1; i <= s; i ++){
            if(val[rpos] > 0)//k为相对位置,这个是解题很重要的一步!!!
                k = ((k + val[rpos] - 2) % mod + mod ) % mod + 1;//
            else
                k = ((k + val[rpos] - 1 ) % mod + mod ) % mod + 1;

            rpos = getpos(k,1,n,1);
        }
        printf("%s %d\n",str[rpos],factorNum[cnt - 1]);
    }
	return 0;
}

求反素数的方法: Max表示的是分解因数的最大个数,pos表示那个分解因数最大个数的那个数。

int Max = 0, pos;
    for(int i = 1; i < n; i ++)
        for(int j = i; j < n; j += i){
            f[j] ++;
            if(f[j] > Max){
                Max = f[j];
                pos = j;
            }
        }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值