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\n", 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\n", ans); 146 } 147 } 148 } 149 return 0; 150 }
转载于:https://www.cnblogs.com/fzl194/p/9034201.html
hdu-3071 Gcd Lcm game---质因数分解+状态压缩+线段树相关推荐
- hdu 1429 胜利大逃亡(续) bfs+状态压缩
胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total S ...
- HDU多校4 - 6992 Lawn of the Dead(线段树+模拟)
题目链接:点击查看 题目大意:给出一个 n∗mn*mn∗m 的矩阵,有 kkk 个点被 banbanban 掉了,现在从点 (1,1)(1,1)(1,1) 出发,只能向右或向下移动,问可以到达的点有多 ...
- HDU 6183 2017广西邀请赛:Color it(线段树)
题目太长了就直接放链接吧 http://acm.hdu.edu.cn/showproblem.php?pid=6183 题意: 一个空的坐标系,有④种操作:①1 x y c表示在(x, y)点染上颜色 ...
- hdu 5584 gcd/lcm/数学公式
input T 1<=T<=1000 x y output 有多少个起点可以走n(n>=0)步走到(x,y),只能从(x,y)走到(x,y+lcm(x,y))/(x+lcm(x,y) ...
- HDU 1565 方格取数(简单状态压缩DP)
http://acm.hdu.edu.cn/showproblem.php?pid=1565 对于每一个数,取或者不取,用0表示不取,1表示取,那么对于每一行的状态,就可以用一个二进制的数来表示.比如 ...
- hdu 1565 方格取数(1)(状态压缩dp)
方格取数(1) Time Limit: 10000/5000 MS (J ...
- 【HDU - 5493】Queue(思维,贪心,线段树)
题干: NN people numbered from 1 to NN are waiting in a bank for service. They all stand in a queue, bu ...
- 【HDU - 3974】 Assign the task (dfs序 + 线段树维护 区间更新+ 单点查询)
题干: There is a company that has N employees(numbered from 1 to N),every employee in the company has ...
- HDU 6194 string string string 后缀数组 + RMQ(线段树)
传送门:HDU6194 题意:问给定字符串中有多少种出现k次的子串. 思路:首先想到后缀数组经典问题,求出现不少于k次的子串的最大长度,类似的这题肯定就是在height数组上搞事情啦. 将height ...
最新文章
- Libevent实现TCP服务循环监听
- 查看dll文件被哪些软件调用的命令
- 关于微信小程序使用wx.downloadFile和wx.getFileSystemManager().saveFile()保存文件在本机找不到文件的说明
- 优化 ASP.NET Core Docker 镜像的大小
- [转] 先验概率与后验概率贝叶斯与似然函数
- 一文看尽科大讯飞年度发布会:医疗,是这家A股AI公司的新赛道
- Navicat for SQL Server Mac 版 SQL 创建工具
- Android属于绑定服务特点是,android – 每个Service绑定是否需要一个ServiceConnection?...
- Linux格式化为物理卷,使用linux的pvs命令格式化输出物理卷信息报表
- 驰为vi10旗舰版linux,驰为Vi10平板电脑完全安装Win10步骤 BY Chinasred
- poi操作word复制表格
- @import ‘./common/stylus/mixins.styl‘引起的一系列错误
- Kbps、KBps是什么意思?网络下载速度单位换算
- android app银联支付,android app 快速接入银联支付流程(android studio版)
- 安卓 ANR 原因,解决方法
- 16.04Ubuntu桌面版搭建
- mac上使用dbeaver设置字体大小
- ACW830. 单调栈
- AGV自动导引运输车
- 玩转华为ENSP模拟器系列 | 配置Dot1q终结子接口接入L2示例