模板___反素数 + poj2286 (反素数+线段树)

来源:互联网 发布:sql合计多个列的值 编辑:程序博客网 时间:2024/05/16 19:47

这篇文章将讨论反素数,素数大家都知道是什么,那么什么又是反素数呢?

先看定义:对于任何一个整数n,将其约数的个数记为g(n),如g(1) = 1,g(6) = 4,并且如果对于任何一个整数i (0<i<n),都满足g(i) < g(n) ,那么我们称n为反素数。

再看性质:1、一个反素数的质因子必然是从2开始的连续质数。

                    2、如果反素数n = 2^a * 3^b * 5^c * 7^d * ......,那么必然a >= b >= c >= d。

                   3,n的因子个数为(a+1)*(b+1)*.....;(排列组合);

最后看一下ACM中有关反素数的一般题型:

                    1、找出不大于n的最大反素数。

                    2、找出不大于n的约数个数最多的数(与1等价)。

                    3、找出一个约数个数为n的最小数。

解题思路:结合反素数的定义及性质,最基本的原理是以2、3、5、......建立搜索树,枚举每一个质因子,然后判断记录。

下面源代码中德AntiPrime[n]代表约数个数为n的最小数:

先求 有cnt个因数的最小整数:

void GetAntiPrime2(lld cur, int cnt, int limit, int k){    //cur:当前枚举到的数    //cnt:该数的因数个数    //limit:因数个数的上限,2^t1*3^t2*5^t3……t1>=t2>=t3……;    //k:第k大的素数    if(cur>((lld)1<<60) || cnt > 150) return;    if(AntiPrime2[cnt]!=0 && AntiPrime2[cnt]>cur)  // 当前的因数个数已经记录过且当时记录的数比当前枚举到的数要大,则替换此因数个数下的枚举到的数        AntiPrime2[cnt] = cur;    if(AntiPrime2[cnt] == 0)                      // 此因数个数的数还没有出现过,则记录        AntiPrime2[cnt] = cur;    lld temp = cur;    for(int i=1; i<=limit; i++)                  // 枚举数,    {        temp = temp*prime2[k];        if(temp > ((lld)1<<60)) return;        GetAntiPrime2(temp, cnt*(i+1), i, k+1);//limit不更新也行    }}


求出来后,自己最好测试下GetAntiPrime数组;里面的值不都是反素数,还要做一个处理:

lld res[888] = {0};    //GetAntiPrime1(1, 0, 1);    GetAntiPrime2(1, 1, 60, 0);    int i = 1;    for(int j = 1; j < 1000; j++) {        if (AntiPrime2[j] == 0) continue;        while(AntiPrime2[j] < res[i-1]) {            i--;        }        if (AntiPrime2[j] > res[i-1])            res[i++] = AntiPrime2[j];    }    i = 1;    while(res[i]) printf("%I64d\n", res[i++]);


res 里面存的才是反素数,但是没存他的因子个数;

前63个反素数和它的因子个数:

1 1
2 2
4 3
6 4
12 6
24 8
36 9
48 10
60 12
120 16
180 18
240 20
360 24
720 30
840 32
1260 36
1680 40
2520 48
5040 60
7560 64
10080 72
15120 80
20160 84
25200 90
27720 96
45360 100
50400 108
55440 120
83160 128
110880 144
166320 160
221760 168
277200 180
332640 192
498960 200
554400 216
665280 224
720720 240
1081080 256
1441440 288
2162160 320
2882880 336
3603600 360
4324320 384
6486480 400
7207200 432
8648640 448
10810800 480
14414400 504
17297280 512
21621600 576
32432400 600
36756720 640
43243200 672
61261200 720
73513440 768
110270160 800
129729600 840
23224320000 850
26011238400 855
42326323200 858
12468461858979840 860
20038599416217600 861

下面是一道相关的例题,HDOJ:4228,时空转移(http://acm.hdu.edu.cn/showproblem.php?pid=4228),题目如下:

Flooring Tiles

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 347    Accepted Submission(s): 137


Problem Description
You want to decorate your floor with square tiles. You like rectangles. With six square flooring tiles, you can form exactly two unique rectangles that use all of the tiles: 1x6, and 2x3 (6x1 is considered the same as 1x6. Likewise, 3x2 is the same as 2x3). You can also form exactly two unique rectangles with four square tiles, using all of the tiles: 1x4, and 2x2.
Given an integer N, what is the smallest number of square tiles needed to be able to make exactly N unique rectangles, and no more, using all of the tiles? If N=2, the answer is 4.
 

Input
There will be several test cases in the input. Each test case will consist of a single line containing a single integer N (1 ≤ N ≤ 75), which represents the number of desired rectangles. The input will end with a line with a single 0.
 

Output
For each test case, output a single integer on its own line, representing the smallest number of square flooring tiles needed to be able to form exactly N rectangles, and no more, using all of the tiles. The answer is guaranteed to be at most 10^18. Output no extra spaces, and do not separate answers with blank lines.
 

Sample Input
216190
 

Sample Output
4840786432
 

题意:

有一些相同的正方形,可以拼出n种不同的长方形,现问如果要拼出n种长方形最少需要的正方形个数。

分析:

可以感觉到,有可能有关整数n的因子,因为都是组成的关系嘛,再仔细一想,其实就是求约数个数为2*n或者2*n-1的最小整数。

源代码:

#include<cstdio>#include<algorithm>#include<cmath>using namespace std;typedef __int64 lld;lld AntiPrime[1010];lld prime[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};void GetAntiPrime(lld cur, int cnt, int limit, int k){    if(cur>((lld)1<<60) || cnt>150) return;    if(AntiPrime[cnt]!=0 && AntiPrime[cnt]>cur)        AntiPrime[cnt] = cur;    if(AntiPrime[cnt] == 0)        AntiPrime[cnt] = cur;    lld temp = cur;    for(int i=1; i<=limit; i++)    {        temp = temp*prime[k];        if(temp > ((lld)1<<60)) return;        GetAntiPrime(temp, cnt*(i+1), i, k+1);    }}int main(){    int n;    GetAntiPrime(1,1,75,0);    for(int i=1;i<=75;i++)    {        if(AntiPrime[i*2-1]!=0 && AntiPrime[i*2]!=0)            AntiPrime[i] = min(AntiPrime[i*2-1],AntiPrime[i*2]);        else if(AntiPrime[i*2]!=0)            AntiPrime[i] = AntiPrime[i*2];        else            AntiPrime[i] = AntiPrime[i*2-1];    }    while(~scanf("%d",&n) && n)        printf("%I64d\n", AntiPrime[n]);    return 0;}

poj 2886 Who Gets the Most Candies?

题意:n个孩子按顺时针排列,每个人受伤都有一张牌,牌上有一个数字,从第k个孩子开始出队,出队的孩子卡上数字是K,则顺时针第k人是下一个出队的,负数则逆时针,第P个出队的会得到的糖果数是p的因子个数,输出得到最多糖果的人和他的糖果数,如果有多个,则输出最闲出队的。

注意一下:n个孩子一开始是顺时针坐的,往左就是顺时针,往右就逆,别弄晕了;

思路:线段树存储的是每个子树中还有几个人,而因子数我们可以用反素数打表求得;

代码:

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>using namespace std;#define lson k<<1, l, mid#define rson k<<1|1, mid+1, rconst int MAXN = 500008;int AntiPrime[40] = {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 Prime[40] = {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};int n, lines[MAXN<<2], a[MAXN];char cc[MAXN][11];void build(int k, int l, int r) {    lines[k] = r-l+1;    if (l == r) return ;    int mid = (l+r)>>1;    build(lson);    build(rson);}int turnr(int step, int k, int l, int r) {    lines[k]--;    if (l == r) return l;    int mid = (l+r)>>1;    if (step <= lines[k<<1])        return turnr(step, lson);    else return turnr(step-lines[k<<1], rson);}int main() {    int k;    //freopen("in.txt", "r", stdin);    while(~scanf("%d%d", &n, &k)) {        build(1, 1, n);        int j = 34, res, p;        while (AntiPrime[j] > n) {            j--;        }        p = AntiPrime[j];//第p个出队的人的因子数是答案        res = Prime[j];//p的因子数;        for(int i = 1; i <= n; i++)            scanf("%s%d", cc[i], a+i);        int pos, m = n;        while (p--) {            n--;//n表示这个圈还有多少人;            pos = turnr(k, 1, 1, m);//这个圈中第k个人一开始时的位置            if (!n) break;            if (a[pos] > 0)                k = (k-2+a[pos])%n+1;//顺时针往左                //因为第上面第k个人出队了,此时的k个人相当于上面的第k个人的左边一个人,所以先减了1;                //涉及模运算令代号从0开始,所以开始要减1            else k = ((k-1+a[pos])%n+n)%n+1;//逆时针        }        printf("%s %d\n", cc[pos], res);    }    return 0;}


0 0
原创粉丝点击