• hdu-3071 Gcd & Lcm game---质因数分解+状态压缩+线段树


    题目链接:

    http://acm.hdu.edu.cn/showproblem.php?pid=3071

    题目大意:

    给定一个长度为n的序列m次操作,操作的种类一共有三种

      • 查询
        • L :查询一个区间的所有的数的最小公倍数modp
        • G :查询一个区间的所有的数的最大公约数modp
      • 修改
        • C :将给定位置的值修改成x

    解题思路:

    注意数据范围,每个数字不超过100,所以100以内的质因子最多25个,如果直接求解lcm和gcd的话,long long也是存不下的,所以采用存储质因子的指数,但是如果每个节点存25个值,不仅会超内存,还会超时,所以采用位运算来存每个质因子出现的次数,大于10的质因子最多出现一次,所以只需要1位即可,小于10的有2 3 5 7,2最多出现6次,即2的6次方64,3最多出现4次,5最多出现2次,7最多出现2次

    所以用3个bit存2的指数,3个bit存3的指数,2个存5,2个存7,其余的只需要1位

    pos数组就存的是这些素数的指数具体存在哪一位

    int prime[] = {2, 3, 5, 7, 11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
    int pos[] =   {28,25,23,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    // 0000 0000 0000 0000 0000 0000 0000 0000
    //    |   |  | |
    //    2   3  5 7
    //用这些位表示各个素数出现的次数

    求解gcd和lcm的时候,需要求出不同素因子之间的最大值和最小值,所以需要对2 3 5 7分别求解

    其余的由于只有1位可以利用&运算求解,

    下面自定义了Min和Max函数,求的就是x和y的gcd和lcm,这里的x和y以及求出的解并不是原来的值,而是存储的是素因子的指数表示的值

    //宏定义的x和y的括号不能省略,因为参数可能是一个表达式,需要加上括号
    #define _min(x, y) ((x) < (y) ? (x) : (y))//写成宏定义更快
    #define _max(x, y) ((x) > (y) ? (x) : (y))
    inline int Min(int x, int y)
    {
        return _min(x&0x70000000, y&0x70000000) | _min(x&0x0e000000, y&0x0e000000) | _min(x&0x01800000, y&0x01800000) | _min(x&0x00600000, y&0x00600000) | ((x&0x001fffff)&(y&0x001fffff));
    }
    inline int Max(int x, int y)
    {
        return _max(x&0x70000000, y&0x70000000) | _max(x&0x0e000000, y&0x0e000000) | _max(x&0x01800000, y&0x01800000) | _max(x&0x00600000, y&0x00600000) | ((x&0x001fffff)|(y&0x001fffff));
    }

    解释一下上面的_min(x&0x70000000, y&0x70000000) 0x70000000 就是16进制的数字,转化成2进制是:

    0111 0000 0000 0000 0000 0000 0000 0000

    由上面可知第31位到28位存的是2的指数,也就是上面红色部分,用x&0x70000000,就求出了x中2的指数,而且把其他位全部置成0,y也是一样,在其中取出最小值,也就是x和y的gcd中2的指数。

    同理求出3 5 7,对于后面的位都是1位,直接用&即可求出最小的指数次数,用 | 求出最大的指数次数。

    还需要两个函数,一个是将数字x进行分解,将其变成上述的形式。

    一个函数是根据上述形式,求出解并模上p。

    inline int turn(int x)//将x质因数分解,并且将指数存在y的每一个bit上
    {
        int y = 0;
        for(int i = 0; i < 25 && x > 1; i++)
        {
            int cnt = 0;
            while(x % prime[i] == 0)
            {
                x /= prime[i];
                cnt++;
            }
            y |= cnt << pos[i];
        }
        return y;
    }
    inline int back(int x, int p)//将所存的指数转化成原来的数字,并且模上p
    {
        ll y = 1;
        int k = x >> pos[0];
        x ^= k << pos[0];//消去2的指数
        while(k--)y = y * prime[0] % p;
        k = x >> pos[1];    x ^= k << pos[1];    while(k--)y = y * prime[1] % p;
        k = x >> pos[2];    x ^= k << pos[2];    while(k--)y = y * prime[2] % p;
        k = x >> pos[3];    x ^= k << pos[3];    while(k--)y = y * prime[3] % p;
        for(int i = 4; i < 25; i++)
            if(x & (1<<pos[i]))y = y * prime[i] % p;
        return y % p;
        //此处还要模上p,因为y最开始赋值为1,没有经过while循环的话,就没有模上p,虽然y为1没关系,但是数据中有p=1的时候,此时y没有模上p
    }

    这两个函数很简单,但是题目很坑,有一个小细节没注意到,WA了一个多小时

    就是back函数的最后一句,我本以为每次运算均已经模上了p,后来偷懒就不模上p,但是这导致我一直WA,细想后发现,最开始y = 1,如果进行while里面的乘法的话就会模上p,但是不进行while乘法,就还是原来的1,看了一眼数据范围发现,这个p可以是1,这样的话,答案就是0了,这就是导致WA的原因,为了找错误还写了个生成测试数据的代码

    剩下的就是普通的线段树了

    这里需要注意的是,这道题时间卡的紧,用内联函数更快,上面的back函数可以优化成下面这个样子,这样会更快。(首先就把2 3 5 7 的i次方算出来,这样可以节省300多ms,因为这几个函数调用太频繁了)

    int a[]={1,2,4,8,16,32,64};
    int b[]={1,3,9,27,81};
    int c[]={1,5,25};
    int d[]={1,7,49};
    inline int back(int x,int p)
    {
        long long y=1;
        int k=x>>dpos[0];y=y*a[k]%p;x^=k<<dpos[0];
        k=x>>dpos[1];y=y*b[k]%p;x^=k<<dpos[1];
        k=x>>dpos[2];y=y*c[k]%p;x^=k<<dpos[2];
        k=x>>dpos[3];y=y*d[k]%p;x^=k<<dpos[3];
        for(int i=4;i<25;i++)
        if(x&(1<<dpos[i])) y=y*prime[i]%p;
        return y;
    }

    Gcd和LCM的查询必须分开写,一开始我只写了一个函数,每次都可以求出两个值,但是一下就超时了。

    最后就是这道题的代码啦

      1 #include<bits/stdc++.h>
      2 #define MID(l, r) (l + (r - l) / 2)
      3 #define lson(o) (o<<1)
      4 #define rson(o) (o<<1|1)
      5 #define _min(x, y) ((x) < (y) ? (x) : (y))//写成宏定义更快
      6 #define _max(x, y) ((x) > (y) ? (x) : (y))
      7 using namespace std;
      8 typedef long long ll;
      9 const int maxn = 1e6 + 10;
     10 int prime[] = {2, 3, 5, 7, 11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
     11 int pos[] =   {28,25,23,21,20,19,18,17,16,15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
     12 // 0000 0000 0000 0000 0000 0000 0000 0000
     13 //    |   |  | |
     14 //    2   3  5 7
     15 //用这些位表示各个素数出现的次数
     16 inline int Min(int x, int y)
     17 {
     18     return _min(x&0x70000000, y&0x70000000) | _min(x&0x0e000000, y&0x0e000000) | _min(x&0x01800000, y&0x01800000) | _min(x&0x00600000, y&0x00600000) | ((x&0x001fffff)&(y&0x001fffff));
     19 }
     20 inline int Max(int x, int y)
     21 {
     22     return _max(x&0x70000000, y&0x70000000) | _max(x&0x0e000000, y&0x0e000000) | _max(x&0x01800000, y&0x01800000) | _max(x&0x00600000, y&0x00600000) | ((x&0x001fffff)|(y&0x001fffff));
     23 }
     24 inline int turn(int x)//将x质因数分解,并且将指数存在y的每一个bit上
     25 {
     26     int y = 0;
     27     for(int i = 0; i < 25 && x > 1; i++)
     28     {
     29         int cnt = 0;
     30         while(x % prime[i] == 0)
     31         {
     32             x /= prime[i];
     33             cnt++;
     34         }
     35         y |= cnt << pos[i];
     36     }
     37     return y;
     38 }
     39 inline int back(int x, int p)//将所存的指数转化成原来的数字,并且模上p
     40 {
     41     ll y = 1;
     42     int k = x >> pos[0];
     43     x ^= k << pos[0];//消去2的指数
     44     while(k--)y = y * prime[0] % p;
     45     k = x >> pos[1];    x ^= k << pos[1];    while(k--)y = y * prime[1] % p;
     46     k = x >> pos[2];    x ^= k << pos[2];    while(k--)y = y * prime[2] % p;
     47     k = x >> pos[3];    x ^= k << pos[3];    while(k--)y = y * prime[3] % p;
     48     for(int i = 4; i < 25; i++)
     49         if(x & (1<<pos[i]))y = y * prime[i] % p;
     50     return y % p;
     51     //此处还要模上p,因为y最开始赋值为1,没有经过while循环的话,就没有模上p,虽然y为1没关系,但是数据中有p=1的时候,此时y没有模上p
     52 }
     53 struct node
     54 {
     55     int l, r;
     56     int gcd, lcm;
     57 }tree[maxn];
     58 int a[maxn];
     59 void build(int o, int l, int r)
     60 {
     61     tree[o].l = l, tree[o].r = r;
     62     if(l == r)
     63     {
     64         tree[o].gcd = tree[o].lcm = turn(a[l]);
     65         return;
     66     }
     67     int m = MID(l ,r), lc = lson(o), rc = rson(o);
     68     build(lc, l, m);
     69     build(rc, m + 1, r);
     70     tree[o].gcd = Min(tree[lc].gcd, tree[rc].gcd);
     71     tree[o].lcm = Max(tree[lc].lcm, tree[rc].lcm);
     72 }
     73 //a[p] = v;
     74 int p, v;
     75 void update(int o)
     76 {
     77     if(tree[o].l == tree[o].r)
     78     {
     79         tree[o].lcm = tree[o].gcd = turn(v);
     80         return;
     81     }
     82     int lc = lson(o), rc = rson(o);
     83     if(p <= tree[lc].r)update(lc);
     84     else update(rc);
     85     tree[o].gcd = Min(tree[lc].gcd, tree[rc].gcd);
     86     tree[o].lcm = Max(tree[lc].lcm, tree[rc].lcm);
     87 }
     88 int Gcd, Lcm;
     89 int ql, qr;
     90 void query_gcd(int o)
     91 {
     92     if(ql <= tree[o].l && qr >= tree[o].r)
     93     {
     94         Gcd = Min(Gcd, tree[o].gcd);
     95         //Lcm = Max(Lcm, tree[o].lcm);
     96         return;
     97     }
     98     int lc = lson(o), rc = rson(o);
     99     if(ql <= tree[lc].r)query_gcd(lc);
    100     if(qr >= tree[rc].l)query_gcd(rc);
    101 }
    102 void query_lcm(int o)
    103 {
    104     if(ql <= tree[o].l && qr >= tree[o].r)
    105     {
    106         //Gcd = Min(Gcd, tree[o].gcd);
    107         Lcm = Max(Lcm, tree[o].lcm);
    108         return;
    109     }
    110     int lc = lson(o), rc = rson(o);
    111     if(ql <= tree[lc].r)query_lcm(lc);
    112     if(qr >= tree[rc].l)query_lcm(rc);
    113 }
    114 int main()
    115 {
    116     //freopen("output.txt", "w", stdout);
    117     int n, q;
    118     while(scanf("%d%d", &n, &q) != EOF)
    119     {
    120         for(int i = 1; i <= n; i++)scanf("%d", &a[i]);
    121         build(1, 1, n);
    122         char s[5];
    123         while(q--)
    124         {
    125             Gcd = 0x7fffffff;
    126             Lcm = 0;
    127             scanf("%s", s);
    128             if(s[0] == 'C')
    129             {
    130                 scanf("%d%d", &p, &v);
    131                 update(1);
    132             }
    133             else if(s[0] == 'L')
    134             {
    135                 scanf("%d%d%d", &ql, &qr, &p);
    136                 query_lcm(1);
    137                 int ans = back(Lcm, p);
    138                 printf("%d
    ", ans);
    139             }
    140             else if(s[0] == 'G')
    141             {
    142                 scanf("%d%d%d", &ql, &qr, &p);
    143                 query_gcd(1);
    144                 int ans = back(Gcd, p);
    145                 printf("%d
    ", ans);
    146             }
    147         }
    148     }
    149     return 0;
    150 }
  • 相关阅读:
    通过了解Servlet和Http之间的关系,了解web中http通信使用(二)
    Java 简单操作hdfs API
    安装Apache-storm-0.9.1-incubating图解教程
    CentOS6.4安装Zookeeper-3.4.12图解教程
    JDBC简单查询数据库
    Windwos上Mysql突然出现系统错误3,找不到系统路口
    利用Javaweb应用中六种属性范围,来理解Servlet的并发问题
    Servlet中分发器和重定向两兄弟
    如何查看服务器机房位置
    解决"应用程序无法启动,因为应用程序的并行配置不正确"问题
  • 原文地址:https://www.cnblogs.com/fzl194/p/9034201.html
Copyright © 2020-2023  润新知