POJ2886 Who Gets the Most Candies?(反素数+线段树模拟约瑟夫环)

本文介绍了一种使用反素数解决POJ2886问题的方法,通过预处理反素数并结合线段树模拟约瑟夫环的跳转过程,实现了高效的求解方案。

传送门:http://poj.org/problem?id=2886
这题真是太厉害了。。
我们要先知道反素数,推荐: http://blog.csdn.net/ACdreamers/article/details/25049767
这题很多人都是打表反素数的,我暴力预处理了一下,虽然有点慢,最后也是4000+ms过的。。
这题的答案一定为反素数,因为小于这个反素数的数的因子数一定比反素数少,所以求出<=n的最大的反素数,就一定是1-n中因子数最多的数字。知道这个结论以后,用线段树模拟一下约瑟夫环的跳转过程。

if(val[pos]>0){
    k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
    //k-1表示去掉本身,val[pos]-1在后面+1回来是为了防止k=0
}
else{
     k=(((k+val[pos]-1)%mod)+mod)%mod+1;
     //往前找的时候k不用去掉,所以k不用-1
 }

然后每次把k更新到线段树里,类似于POJ 2828 排队那题

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define pb push_back
#define mp make_pair
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define calm (l+r)>>1
const int INF = 1e9+7;

const int maxn=500010;

bool vis[110];
vector<int> prime;
int antiprime[300],num[300];
void init(){//打表反素数
    for(int i=2; i<=50; i++){
        if(!vis[i]){
            prime.pb(i);
            for(int j=i+i; j<=50; j+=i){
                vis[j]=true;
            }
        }
    }
    memset(antiprime,127,sizeof antiprime);
    antiprime[1]=1;
    for(int i=1;i<=maxn;i++){
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(i%prime[j]==0){
                int tot=1,t=i;
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
            antiprime[ans]=min(antiprime[ans],i);
        }
    }
    sort(antiprime,antiprime+300);
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299])break;
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(antiprime[i]%prime[j]==0){
                int tot=1,t=antiprime[i];
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
        }
        num[i]=ans;
    }
    int now=0;
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299]){break;}
        if(num[i]<=now)antiprime[i]=antiprime[299];
        now=max(now,num[i]);
    }
    sort(antiprime,antiprime+300);
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299]){break;}
    }
    for(int i=0;i<300;i++){
        if(antiprime[i]==antiprime[299])break;
        int ans=1;
        for(int j=0,len=prime.size(); j<len; j++){
            if(antiprime[i]%prime[j]==0){
                int tot=1,t=antiprime[i];
                while(t%prime[j]==0){
                    t/=prime[j];tot++;
                }
                ans*=tot;
            }
        }
        num[i]=ans;
    }
}

int n,k;
char name[maxn][13];
int val[maxn];
int sum[maxn<<2];
void build(int l,int r,int rt){
    sum[rt]=r-l+1;
    if(l==r){
        return;
    }
    int m=calm;
    build(lson);build(rson);
}
int update(int x,int l,int r,int rt){
    sum[rt]--;
    if(l==r)return l;
    int m=calm;
    if(x<=sum[rt<<1])return update(x,lson);
    return update(x-sum[rt<<1],rson);
}
int main()
{
    //freopen("D://input.txt","r",stdin);
    init();
    while(scanf("%d%d",&n,&k)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%s%d",name[i],&val[i]);
        }
        int cnt=0;
        for(int i=0;i<35;i++){
        //答案一定为反素数,因为小于这个反素数的数的因子数一定比反素数少
            if(antiprime[i]<=n)cnt=i;
        }
        //printf("%d\n",antiprime[cnt]);
        build(1,n,1);
        int &mod=sum[1];//圈内总人数
        int pos=0;
        val[0]=0;//先等于0,使第一个人为k
        for(int i=0;i<antiprime[cnt];i++){
            if(val[pos]>0){
                k=(((k-1+val[pos]-1)%mod)+mod)%mod+1;
                //k-1表示去掉本身,val[pos]-1在后面+1回来是为了防止k=0
            }
            else{
                k=(((k+val[pos]-1)%mod)+mod)%mod+1;
                //往前找的时候k不用去掉,所以k不用-1
            }
            pos=update(k,1,n,1);
        }
        printf("%s %d\n",name[pos],num[cnt]);
    }
    return 0;
}
内容概要:本文是一份锂电池基础知识的学习课件,系统介绍了锂电池的种类、方形电池的结构与制造工艺流程,以及出货不良的常见类型与分析。文章首先按形状和材料体系对方形、圆柱、软包等锂电池进行分类,并重点对比了钴酸锂、锰酸锂、三元材料和磷酸铁锂在电压、能量密度、循环寿命、成本和安全性等方面的差异。随后详细阐述了方形电池的内部结构,包括正负极柱、盖板组件、防爆阀、极组和隔膜等关键部件的功能与设计原理。在工艺部分,全面讲解了从匀浆、涂布、辊压、模切到装配、焊接、注液、化成等全流程的关键步骤、技术参数与质量控制要点,尤其对叠片与卷绕工艺进行了深入对比。最后,针对生产中常见的出货不良问题,如厚度、电压、容量、外观等方面异常,进行了归因分析与改进方向说明。; 适合人群:从事锂电池研发、生产、品质管理等相关工作的技术人员,以及对电池制造工艺感兴趣的工程类学生或初学者。; 使用场景及目标:①用于锂电池生产工艺培训与知识普及;②作为现场工艺优化与不良问题分析的参考依据;③帮助理解电池结构设计与性能之间的关系,提升工艺控制能力。; 阅读建议:建议结合实际生产流程图与设备操作规范对照学习,重点关注各工艺环节的技术参数设定与失效模式,便于在实际工作中快速定位和解决质量问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值