HDU_3071 Gcd & Lcm game 【素数分解 + 线段树 + 状压】

一、题目

   Gcd & Lcm game

二、分析

  非常好的一题。

  首先考虑比较暴力的做法,肯定要按区间进行处理,对于$lcm$和$gcd$可以用标准的公式进行求,但是求$lcm$的时候是肯定会爆$long long$的。

  考虑用素数分解,将所有的数分解后,发现素因子的个数有限,且每个因子的幂也有限,最多的也就是$2^_6$,然后可以考虑将素因子用二进制的每一位进行表示。对于$2,3,5,7$可能会要的多点,所以多给给几位就可以了,最后发现,刚好可以$32$位以内。

  这里就需要写两个转换函数,然后利用$gcd$和$lcm$的性质进行求解和变换。最后考虑区间查询和单点修改,再用一个线段树即可。  

三、AC代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
#define ll long long
#define Min(a,b) ((a)>(b)?(b):(a))
#define Max(a,b) ((a)>(b)?(a):(b))
#define lson (rt<<1)
#define rson (rt<<1|1)
const int MAXN = 1e5;
struct Node
{
    int L, G;
}segTree[MAXN<<2];
int Prime[25] = {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[25] = {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};

inline int Gcd(int x, int y)
{
    //最后是相&,如果继续用Min,相当于只用了一个导致WA
    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 Lcm(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));
}
//将x质因素分解,并用二进制表示
inline int Turn(int x)
{
    int res, ans = 0;
    for(int i = 0; i < 25 && x > 1; i++) {
        res = 0;
        while(x%Prime[i] == 0) {
            res++;
            x/=Prime[i];
        }
        ans |= (res<<Pos[i]);
    }
    return ans;
}

int Mi2[] = {1, 2, 4, 8, 16, 32, 64};
int Mi3[] = {1, 3, 9, 27, 81};
int Mi5[] = {1, 5, 25};
int Mi7[] = {1, 7, 49};

//将二进制表示的数转转换成原来的数并取模
inline int Get(int x, int p)
{
    ll ans = 1;
    int res = x>>Pos[0];
    x ^= res<<Pos[0];   //消去表示2的位上的数
    ans = ans*Mi2[res]%p;
    //求3的指数并消去
    res = x>>Pos[1];    x ^= res<<Pos[1];   ans = ans * Mi3[res] % p;
    //求5的指数并消去
    res = x>>Pos[2];    x ^= res<<Pos[2];   ans = ans * Mi5[res] % p;
    //求7的指数并消去
    res = x>>Pos[3];    x ^= res<<Pos[3];   ans = ans * Mi7[res] % p;
    for(int i = 4; i < 25; i++) {
        if((x>>Pos[i])&1) {
            ans = ans * Prime[i] % p;
        }
    }
    return ans % p;
}

void Push_up(int rt)
{
    segTree[rt].G = Gcd(segTree[lson].G, segTree[rson].G);
    segTree[rt].L = Lcm(segTree[lson].L, segTree[rson].L);
    return;
}

void Build(int rt, int l, int r)
{
    if(l == r) {
        int a;
        scanf("%d", &a);
        segTree[rt].G = Turn(a);
        segTree[rt].L = Turn(a);
        return;
    }
    int mid = (l + r) >> 1;
    Build(lson, l, mid);
    Build(rson, mid + 1, r);
    Push_up(rt);
}

void Change(int rt, int l, int r, int pos, int val)
{
    if(l == r) {
        segTree[rt].G = Turn(val);
        segTree[rt].L = segTree[rt].G;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) {
        Change(lson, l, mid, pos, val);
    }
    else if (pos > mid) {
        Change(rson, mid + 1, r, pos, val);
    }
    Push_up(rt);
}

int anslcm, ansgcd;
void Query_lcm(int rt, int l, int r, int L, int R)
{
    if(L <= l && r <= R) {
        anslcm = Lcm(anslcm, segTree[rt].L);
        return;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) {
        Query_lcm(lson, l, mid, L, R);
    }
    if(R > mid) {
        Query_lcm(rson, mid + 1, r, L, R);
    }
}
void Query_gcd(int rt, int l, int r, int L, int R)
{
    if(L <= l && r <= R) {
        ansgcd = Gcd(ansgcd, segTree[rt].G);
        return;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) {
        Query_gcd(lson, l, mid, L, R);
    }
    if(R > mid) {
        Query_gcd(rson, mid + 1, r, L, R);
    }
}
int main()
{
    //freopen("input.txt", "r", stdin);
    int N, Q;
    while(scanf("%d%d", &N, &Q) != EOF) {
        Build(1, 1, N);
        char s[2];
        for(int i = 0; i < Q; i++) {
            scanf("%s", s);
            int a, b, c;
            if(s[0] == ‘L‘) {
                anslcm = 0;
                scanf("%d%d%d", &a, &b, &c);
                Query_lcm(1, 1, N, a, b);
                int ans = Get(anslcm, c);
                printf("%d\n", ans);
            }
            else if(s[0] == ‘G‘) {
                ansgcd = 0x7fffffff;
                scanf("%d%d%d", &a, &b, &c);
                Query_gcd(1, 1, N, a, b);
                int ans = Get(ansgcd, c);
                printf("%d\n", ans);
            }
            else {
                scanf("%d%d", &a, &c);
                Change(1, 1, N, a, c);
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/dybala21/p/11431591.html

时间: 2024-11-08 09:00:04

HDU_3071 Gcd & Lcm game 【素数分解 + 线段树 + 状压】的相关文章

HDU 5023 线段树+状压

2014 ACM/ICPC Asia Regional Guangzhou Online N个节点,M个操作. P操作把l-r全部改为x (1<=x<=30) Q操作询问l-r出现的数字,升序输出 线段树成端更新+成端查找 因为X最大30,用二进制压缩存储已经出现的数字. #include "stdio.h" #include "string.h" struct node { int l,r; __int64 x,lazy; }data[4001000]

HDU4497 GCD and LCM 数论 素数分解

题意很简单首先以前做最简单的LCM跟CGD的时候都知道先求出两个数A,B的最大公约数GCD,那么LCM可以利用  A*B/GCD来求得,这点一开始脑残了没想到,结果没有进行特盘所以错了,意思就是 题目给的L%G不为0的话就是无解,结果我给判其它的去了,肯定漏了些什么没有发现 然后对于 L/G进行素因子分解,同时任意的数都能够通过素因子分解来表示,所以三个解x,y,z也能分解 L/G = p1^q1*p2^q2.... x = p1^i1*... y = p1^j1*... z = p1^k1*.

线段树(压位)luogu P1558色板游戏

题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, ... L. 现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事: "C A B C" 指在A到 B 号方格中涂上颜色 C. "P A B" 指老师的提问:A到 B号方格中有几种颜色. 学校的颜料盒中一共有 T 种颜料.为简便起见,我们把他们标记为 1, 2, ... T. 开始时色板上原

zoj3886--Nico Number(素数筛+线段树)

Nico Number Time Limit: 2 Seconds      Memory Limit: 262144 KB Kousaka Honoka and Minami Kotori are playing a game about a secret of Yazawa Nico. When the game starts, Kousaka Honoka will give Minami Kotori an array A of N non-negative integers. Ther

当前插入的线段能完整覆盖存在的几条线段 树状数组 HDU 5372 Segment Game

http://acm.hdu.edu.cn/showproblem.php? pid=5372 Segment Game Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 1284    Accepted Submission(s): 375 Problem Description Lillian is a clever girl so

hdu 4497 GCD and LCM 数论 素数分解

GCD and LCM Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) Total Submission(s): 1339    Accepted Submission(s): 607 Problem Description Given two positive integers G and L, could you tell me how many solutions of

BZOJ2595: [Wc2008]游览计划(斯坦纳树,状压DP)

Time Limit: 10 Sec  Memory Limit: 256 MBSec  Special JudgeSubmit: 2030  Solved: 986[Submit][Status][Discuss] Description Input 第一行有两个整数,N和 M,描述方块的数目. 接下来 N行, 每行有 M 个非负整数, 如果该整数为 0, 则该方块为一个景点:否则表示控制该方块至少需要的志愿者数目. 相邻的整数用 (若干个) 空格隔开,行首行末也可能有多余的空格. Outpu

BZOJ4006: [JLOI2015]管道连接(斯坦纳树,状压DP)

Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 1171  Solved: 639[Submit][Status][Discuss] Description 小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰. 该部门有 n 个情报站,用 1 到 n 的整数编号.给出 m 对情报站 ui;vi 和费用 wi,表示情 报站 ui 和 vi 之间可以花费 wi 单位资源建立通道. 如果一个情报站经过若干个建立好的通道可以到达另外一个情报

【BZOJ4373】算术天才⑨与等差数列 线段树+set

[BZOJ4373]算术天才⑨与等差数列 Description 算术天才⑨非常喜欢和等差数列玩耍.有一天,他给了你一个长度为n的序列,其中第i个数为a[i].他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列.当然,他还会不断修改其中的某一项.为了不被他鄙视,你必须要快速并正确地回答完所有问题.注意:只有一个数的数列也是等差数列. Input 第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数.第二行