poj2886 Who Gets the Most Candies? 线段树加反素数

刚看到题第一想法一个人一个人往下推,估计会超时,上网看了一些前辈的算法,发现了反素数这个神奇的东西,然后就保存了一份代码作为模板,求反素数以及其约数的个数,虽然还没有仔细看,但起码以后能用来打表了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;

typedef long long lld;

lld prime[20]={2,3,5,7,11,13,17,19,23,29,31,37,39,41,43,47,53};
lld n;
lld bestcurr,largecnt;//bestcurr 相同最大因数个数中值最小的数,largecnt:n范围内最大的因数个数
void getarcprime(lld curr,int cnt,int limit,int k)
{
    if(curr>n)
        return ;
    if(largecnt<cnt)//此时枚举到的因数个数比之前记录的最大的因数个数要大,就替换最大因数个数
    {
        largecnt=cnt;
        bestcurr=curr;
    }
    if(largecnt==cnt && bestcurr>curr)//替换最优值
        bestcurr=curr;
    lld temp=curr;
    for(int i=1;i<=limit;i++)
    {
        temp=temp*prime[k];
        if(temp>n)
            return;
        getarcprime(temp,cnt*(i+1),i,k+1);

    }
}
int main()
{
    int i,cas,ans;
    int a[100],b[100];
    ans = 0;
    int j = 0;
    scanf("%d",&cas);
    for(i=1;i<=cas;i++)
    {
        n = i;
        bestcurr=0;
        largecnt=0;
        getarcprime(1,1,50,0);
        if(ans != bestcurr)
        {
            ans = bestcurr;
            a[j] = bestcurr;
            b[j++] = largecnt;
        }
    }
    for(int i = 0; i < j; i++)
        printf("%d ",a[i]);
    printf("\n");

    for(int i = 0; i < j; i++)
        printf("%d ",b[i]);
    return 0;
}

再说这道题,用线段树记录每个位置有没有孩子,记录区间孩子的个数和,然后去寻找孩子原来的位置

假设一开始是k位置的孩子跳出去,然后k这个位置的sum--,代表这里没有孩子了,然后推出下一个该跳出去的孩子是当前的第几个(去掉第k个孩子的当前),然后再用query求出是整个序列的第几个,一直到约数最多的那个数

AC代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#define ls l,m,nod<<1
#define rs m+1,r,nod<<1|1
#define maxn 500010
using namespace std;
struct Node{
    int l;
    int r;
    int sum;
}node[maxn<<2];
int n,k,v[maxn];
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};
char peo[maxn][11];
void build(int l,int r,int nod){
    node[nod].l = l;
    node[nod].r = r;
    node[nod].sum = r-l+1;
    if(l == r) return;
    int m = (l + r)>>1;
    build(ls);
    build(rs);
}
int query(int k,int nod){
    node[nod].sum--;
    if(node[nod].l == node[nod].r)
        return node[nod].l;
    if(k <= node[nod<<1].sum)
        return query(k,nod<<1);
    else
        return query(k - node[nod<<1].sum,nod<<1|1);
}

int main()
{
    int p,ppp;
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        for(int i = 1; i <= n; i++)
        {
            scanf("%s %d",peo[i],&v[i]);
        }
        for(int i = 0; i < 37; i++)
            if(a[i] > n)
        {
            p = a[i - 1];
            ppp = b[i-1];
            break;
        }
        build(1,n,1);
        int tem;
        for(int i = 0; i < p; i++)
        {
            //printf("k == %d\n",k);
            tem = query(k,1);
            //printf("tem== %d\n",tem);
            n--;
            if(n == 0) break;
            v[tem] = v[tem] > 0?v[tem]:(v[tem] + 1 + n);
            k = (k - 1 -1 + n + (v[tem])%n + n)%n + 1;
        }
        //int ans = query(k,1);
        printf("%s %d\n",peo[tem],ppp);
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值