题目链接:

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[] = {, , , , ,,,,,,,,,,,,,,,,,,,,};
int pos[] = {,,,,,,,,,,,,,,, , , , , , , , , , };
// 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进制是:

0 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 = ;
for(int i = ; i < && x > ; i++)
{
int cnt = ;
while(x % prime[i] == )
{
x /= prime[i];
cnt++;
}
y |= cnt << pos[i];
}
return y;
}
inline int back(int x, int p)//将所存的指数转化成原来的数字,并且模上p
{
ll y = ;
int k = x >> pos[];
x ^= k << pos[];//消去2的指数
while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
for(int i = ; i < ; i++)
if(x & (<<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[]={,,,,,,};
int b[]={,,,,};
int c[]={,,};
int d[]={,,};
inline int back(int x,int p)
{
long long y=;
int k=x>>dpos[];y=y*a[k]%p;x^=k<<dpos[];
k=x>>dpos[];y=y*b[k]%p;x^=k<<dpos[];
k=x>>dpos[];y=y*c[k]%p;x^=k<<dpos[];
k=x>>dpos[];y=y*d[k]%p;x^=k<<dpos[];
for(int i=;i<;i++)
if(x&(<<dpos[i])) y=y*prime[i]%p;
return y;
}

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

最后就是这道题的代码啦

 #include<bits/stdc++.h>
#define MID(l, r) (l + (r - l) / 2)
#define lson(o) (o<<1)
#define rson(o) (o<<1|1)
#define _min(x, y) ((x) < (y) ? (x) : (y))//写成宏定义更快
#define _max(x, y) ((x) > (y) ? (x) : (y))
using namespace std;
typedef long long ll;
const int maxn = 1e6 + ;
int prime[] = {, , , , ,,,,,,,,,,,,,,,,,,,,};
int pos[] = {,,,,,,,,,,,,,,, , , , , , , , , , };
// 0000 0000 0000 0000 0000 0000 0000 0000
// | | | |
// 2 3 5 7
//用这些位表示各个素数出现的次数
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));
}
inline int turn(int x)//将x质因数分解,并且将指数存在y的每一个bit上
{
int y = ;
for(int i = ; i < && x > ; i++)
{
int cnt = ;
while(x % prime[i] == )
{
x /= prime[i];
cnt++;
}
y |= cnt << pos[i];
}
return y;
}
inline int back(int x, int p)//将所存的指数转化成原来的数字,并且模上p
{
ll y = ;
int k = x >> pos[];
x ^= k << pos[];//消去2的指数
while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
k = x >> pos[]; x ^= k << pos[]; while(k--)y = y * prime[] % p;
for(int i = ; i < ; i++)
if(x & (<<pos[i]))y = y * prime[i] % p;
return y % p;
//此处还要模上p,因为y最开始赋值为1,没有经过while循环的话,就没有模上p,虽然y为1没关系,但是数据中有p=1的时候,此时y没有模上p
}
struct node
{
int l, r;
int gcd, lcm;
}tree[maxn];
int a[maxn];
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
if(l == r)
{
tree[o].gcd = tree[o].lcm = turn(a[l]);
return;
}
int m = MID(l ,r), lc = lson(o), rc = rson(o);
build(lc, l, m);
build(rc, m + , r);
tree[o].gcd = Min(tree[lc].gcd, tree[rc].gcd);
tree[o].lcm = Max(tree[lc].lcm, tree[rc].lcm);
}
//a[p] = v;
int p, v;
void update(int o)
{
if(tree[o].l == tree[o].r)
{
tree[o].lcm = tree[o].gcd = turn(v);
return;
}
int lc = lson(o), rc = rson(o);
if(p <= tree[lc].r)update(lc);
else update(rc);
tree[o].gcd = Min(tree[lc].gcd, tree[rc].gcd);
tree[o].lcm = Max(tree[lc].lcm, tree[rc].lcm);
}
int Gcd, Lcm;
int ql, qr;
void query_gcd(int o)
{
if(ql <= tree[o].l && qr >= tree[o].r)
{
Gcd = Min(Gcd, tree[o].gcd);
//Lcm = Max(Lcm, tree[o].lcm);
return;
}
int lc = lson(o), rc = rson(o);
if(ql <= tree[lc].r)query_gcd(lc);
if(qr >= tree[rc].l)query_gcd(rc);
}
void query_lcm(int o)
{
if(ql <= tree[o].l && qr >= tree[o].r)
{
//Gcd = Min(Gcd, tree[o].gcd);
Lcm = Max(Lcm, tree[o].lcm);
return;
}
int lc = lson(o), rc = rson(o);
if(ql <= tree[lc].r)query_lcm(lc);
if(qr >= tree[rc].l)query_lcm(rc);
}
int main()
{
//freopen("output.txt", "w", stdout);
int n, q;
while(scanf("%d%d", &n, &q) != EOF)
{
for(int i = ; i <= n; i++)scanf("%d", &a[i]);
build(, , n);
char s[];
while(q--)
{
Gcd = 0x7fffffff;
Lcm = ;
scanf("%s", s);
if(s[] == 'C')
{
scanf("%d%d", &p, &v);
update();
}
else if(s[] == 'L')
{
scanf("%d%d%d", &ql, &qr, &p);
query_lcm();
int ans = back(Lcm, p);
printf("%d\n", ans);
}
else if(s[] == 'G')
{
scanf("%d%d%d", &ql, &qr, &p);
query_gcd();
int ans = back(Gcd, p);
printf("%d\n", ans);
}
}
}
return ;
}

hdu-3071 Gcd & Lcm game---质因数分解+状态压缩+线段树的更多相关文章

  1. 【HDOJ】3071 Gcd & Lcm game

    刚开始看这个题目,觉得没法做.关键点是数据小于100.因此,可以枚举所有小于100的素因子进行位压缩.gcd就是求最小值,lcm就是求最大值.c++有时候超时,g++800ms.线段树可解. /* 3 ...

  2. [hdu 6069]素数筛+区间质因数分解

    给[L,R]区间的每一个数都质因数分解的复杂度可以达到(R-L)logR,真的涨姿势…… 另外,质因数分解有很重要的一点,就是只需要打sqrt(R)以内的素数表就够了……因为超过sqrt(R)的至多只 ...

  3. HDU 3605:Escape(最大流+状态压缩)

    http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意:有n个人要去到m个星球上,这n个人每个人对m个星球有一个选择,即愿不愿意去,"Y" ...

  4. HDU 2809 God of War(DP + 状态压缩)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2809 题目大意:给出战神吕布的初始攻击力ATI.防御力DEF.生命值HP.每升一级增加的攻击力In_A ...

  5. hdu 4352 XHXJ's LIS 数位dp+状态压缩

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4352 XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others ...

  6. hdu - 1429 胜利大逃亡(续) (bfs状态压缩)

    http://acm.hdu.edu.cn/showproblem.php?pid=1429 终于开始能够做状态压缩的题了,虽然这只是状态压缩里面一道很简单的题. 状态压缩就是用二进制的思想来表示状态 ...

  7. HDU 3681 Prison Break(BFS+二分+状态压缩DP)

    Problem Description Rompire is a robot kingdom and a lot of robots live there peacefully. But one da ...

  8. HDU 5768 Lucky7 (容斥原理 + 中国剩余定理 + 状态压缩 + 带膜乘法)

    题意:……应该不用我说了,看起来就很容斥原理,很中国剩余定理…… 方法:因为题目中的n最大是15,使用状态压缩可以将所有的组合都举出来,然后再拆开成数组,进行中国剩余定理的运算,中国剩余定理能够求出同 ...

  9. HDU - 6185 :Covering(矩阵乘法&状态压缩)

    Bob's school has a big playground, boys and girls always play games here after school. To protect bo ...

随机推荐

  1. python 多窗口编辑

    同时打开多个文件: 1,vim filename1 filename2 在打开的多个文件中 :next 转到下个文件中 :prev 转到上个文件中 :last/:first 分别到最后一个和第一个文件 ...

  2. 从模板模式到JdbcTemplate

    模板模式初探 关于模板模式,大家可以参阅 模板方法模式深度解析(一) 原始的jdbc 关于原始的jdbc,如下: import java.sql.Connection; import java.sql ...

  3. python爬虫 - python requests网络请求简洁之道

    http://blog.csdn.net/pipisorry/article/details/48086195 requests简介 requests是一个很实用的Python HTTP客户端库,编写 ...

  4. Unix/Linux中的read和write函数

    文件描述符 对于内核而言,所有打开的文件都通过文件描述符引用.文件描述符是一个非负整数.当打开一个现有文件或创建一个新文件时,内核向进程返回一个文件描述符.当读或写一个文件时,使用open或creat ...

  5. LeetCode之“散列表”:Valid Sudoku

    题目链接 题目要求: Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku boar ...

  6. Linux其他常见压缩备份工具 - dd,cpio

    dd dd 可以读取磁碟装置的内容(几乎是直接读取磁区"sector"),然后将整个装置备份成一个文件呢!真的是相当的好用啊- dd 的用途有很多啦-但是我们仅讲一些比较重要的选项 ...

  7. linux下D盘(适用于U盘、硬盘等一切移动存储设备)策略(比格式化猛,因为是不可恢复!)

    关于这样的资料,在百度上还是比较少的,今天就共享出来,在电脑主机上插上你的U盘,输入以下命令: dd if=/dev/zero of=/dev/sdb  bs=1024 count=102400   ...

  8. 高通Android display架构分析

    目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...

  9. android studio设置代理更新

    我们都知道Android Studio是基于IDEA开发的,而我们写的每一个程序又都是有Gradle构建的,Gradle的优点可以说是很多,被很多程序员夸得没边,但是它有一个特点还是值得我们注意的.我 ...

  10. <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步(1)

    <<操作系统精髓与设计原理>>读书笔记(一) 并发性:互斥与同步 并发问题是所有问题的基础,也是操作系统设计的基础.并发包括很多设计问题,其中有进程间通信,资源共享与竞争,多个 ...