AcDream 1732 最小公倍数【位压缩+暴力】

最小公倍数

Time Limit: 10000/5000MS (Java/Others)  Memory Limit: 262144/131072KB (Java/Others)
Problem Description
两个数的最小公倍数就是就是一个最小的可以被这两个数都整除的整数,当然多个数也有最小公倍数,我们现在就来计算一下1~n的最小公倍数是多少吧~
Input
第一行有个数字T.( T≤10000)表示有T组测试数据。 接下来有T行,每行有个整数n.( 2≤n≤10^8).
Output
输出 LCM(1,2,3,4,5,……,n-1, n)对2^32取模的值。
Sample Input
5
10
5
200
15
20
Sample Output
2520
60
2300527488
360360
232792560
Source
第九届北京化工大学程序设计竞赛
Manager

①首先我们分析,Lcm的结果是随着n增大而增大的,而又考虑其数字本身的性质,我们不难发现,只有当n为素数或者是n为素幂次数(2,4,9.....)的时候,Lcm的Ans才会增加。

比如10以内的结果:

1->1

2->2

3->6

4->12

5->60

6->60

7->420

8->840

9->2520

10->2520


②那么接下来我们只要去想办法筛出素数即可,我们用筛法就行,过程中我们每一次取一个素数,然后去将其倍数都置为合数,那么我们如果能够存所有的合数,剩下的数就是素数了。很显然我们需要一个数组Is_or【i】,表示这个数是否为合数(素数);

但是如果直接去开内存的话,可能是不够用。

官方题解的思路是说,我们开一个数组用位压缩去优化内存。开一个char Is_or【i】,去位压缩的优化内存。

我们考虑拆分一个数,假设一个数的二进制表示是1111111000111,那么我们将其拆成两部分:1111111000,111.那么我们将Is_or【1111111000】|=(1<<111)的话,我们就能存以i为前缀,所有的合数的后缀信息。

那么如果我们要判定一个数是否为素数,我们只要将其拆出前缀和后缀,然后判定一下Is_or【前缀】&(1<<后缀)是否为0即可,如果是0,那么这个数就是素数。


过程模拟一下筛法就行了,优化的难点就在于位压缩优化内存。


那么最后我们O(1e8)去离线处理,将所有的答案都暴力求出来就行了。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
using namespace std;
struct node
{
    int val;
    int pos;
}a[150000];
struct node2
{
    int number;
    int gen;
}Add[150000];
unsigned int output[150000];
int cnt;
const int Maxn=100000000;
char Is_or[(Maxn>>3)+50];// 二进制存合数。
int cmp(node a,node b)
{
    return a.val<b.val;
}
int cmp2(node2 a,node2 b)
{
    return a.number<b.number;
}
int Is_prime(int num)
{
    int back=num&7;
    int pre=num>>3;
    if((Is_or[pre]&(1<<back))==0)return 1;
    else return 0;
}
void Set(int num)
{
    int back=num&7;
    int pre=num>>3;
    Is_or[pre]|=(1<<back);

}
void Init()
{
    cnt=0;
    memset(Is_or,0,sizeof(Is_or));
    for(int i=2;i<=sqrt(Maxn);i++)
    {
        if(Is_prime(i)==1)
        {
            for(int j=i*2;j<Maxn;j+=i)
            {
                Set(j);
            }
            long long int j=i*i;
            while(j<(long long int)Maxn)
            {
                Add[cnt].number=j;
                Add[cnt++].gen=i;
                j*=i;
            }
        }
    }
}
int main()
{
    Init();
    int t;
    scanf("%d",&t);
    for(int i=0;i<t;i++)
    {
        scanf("%d",&a[i].val);
        a[i].pos=i;
    }
    sort(a,a+t,cmp);
    sort(Add,Add+cnt,cmp2);
    int j=0;
    int now=0;
    unsigned int ans=1;
    for(int i=1;i<=Maxn;i++)
    {
        if(Is_prime(i)==1)ans*=i;
        while(j<cnt&&Add[j].number==i)ans*=Add[j].gen,j++;
        while(now<t&&a[now].val==i)output[a[now].pos]=ans,now++;
    }
    for(int i=0;i<t;i++)
    {
        printf("%u\n",output[i]);
    }
}













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值