题目链接:

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. 开源视频监控系统:iSpy

    iSpy是一个开源的视频监控软件,目前已经支持中文.自己用了一下,感觉还是很好用的.翻译了一下它的介绍. iSpy将PC变成一个完整的安全和监控系统 iSpy使用您的摄像头和麦克风来检测和记录声音或运 ...

  2. mysql进阶(十八)完全卸载mysql数据库图文教程

    完全卸载mysql数据库图文教程 有时候MySQL不能完全卸载,这时候必须通过一些途径删除掉注册表和一些残余的文件,然后才能重新安装才可以成功! 方法/步骤 1.控制面板-->所有控制面板项-- ...

  3. 写的还不错的专题,android性能优化

    http://www.trinea.cn/android/android-performance-demo/

  4. 图像检索:CEDD(Color and Edge Directivity Descriptor)算法

    颜色和边缘的方向性描述符(Color and Edge Directivity Descriptor,CEDD) 本文节选自论文<Android手机上图像分类技术的研究>. CEDD具有抽 ...

  5. kettle简介(整体架构,运行方式,使用方法)

    项目负责人Matt的说法:把各种数据放到一个壶里,然后呢,以一种你希望的格式流出.呵呵,外国人都很有联想力.看了提供的文档,然后对发布程序的简单试用后,可以很清楚得看到Kettle的四大块: Chef ...

  6. Android ROM开发(一)——Windows下Cygwin和Android_Kitchen厨房的安装

    Android ROM开发(一)--Windows下Cygwin和Android_Kitchen厨房的安装 很久没有碰到ROM开发了,在很久很久以前也是从ROM起步的,无奈还是一脚踏上了Android ...

  7. TCP连接建立系列 — 服务端发送SYNACK段

    本文主要分析:服务器端如何构造和发送SYNACK段. 内核版本:3.6 Author:zhangskd @ csdn blog 发送入口 tcp_v4_send_synack()用于发送SYNACK段 ...

  8. UIScrollView的无限左滑轮播一点也不难

    UIScrollView的轮播在如今的app中用得十分广泛,最初实现的时候方式比较拙劣,滚动到最后一个视图时再返回到第一个看起来非常的不连贯. 今天查询UIScrollView轮播资料,总结两种比较喜 ...

  9. Django之admin的使用和源码剖析

    admin组件使用 Django 提供了基于 web 的管理工具. Django 自动管理工具是 django.contrib 的一部分.你可以在项目的 settings.py 中的 INSTALLE ...

  10. edit distance(编辑距离,两个字符串之间相似性的问题)

    Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2 ...