P1890 gcd区间 线段树

题目描述

给定一行\(n\)个正整数\(a[1]..a[n]\)。

\(m\)次询问,每次询问给定一个区间\([L,R]\),输出\(a[L]..a[R]\)的最大公因数。

输入格式

第一行两个整数\(n,m\)。

第二行n个整数表示\(a[1]..a[n]\)。

以下\(m\)行,每行\(2\)个整数表示询问区间的左右端点。

保证输入数据合法。

输出格式

共m行,每行表示一个询问的答案。

输入输出样例

输入 #1

5 3
4 12 3 6 7
1 3
2 3
5 5

输出 #1

1
3
7

说明/提示

对于30%的数据,\(n <= 100, m <= 10\)

对于60%的数据,\(m <= 1000\)

对于100%的数据,\(1 <= n <= 1000,1 <= m <= 1,000,000\)

 0 < 数字大小 <= 1,000,000,000

题解:

这里提供一种结构体指针线段树的写法:

做这道题,你首先要知道\(gcd\)的求法,由欧几里得算法可知:

int gcd(int x, int y) { return y == 0 ? x : gcd(y, x % y); }

其次,\(gcd\)满足区间可加性,即:
\[
gcd(l, r) = gcd(gcd(l, k), gcd(k+1, r)),k\in[l, r]
\]
线段树直接维护即可...

code:

#include <iostream>
#include <cstdio>
using namespace std;
int read() {
    int x = 0, f = 1; char ch;
    while(! isdigit(ch = getchar())) (ch == '-') && (f = -f);
    for(x = ch^48; isdigit(ch = getchar()); x = (x<<3) + (x<<1) + (ch^48));
    return x * f;
}
int n, m;
inline int gcdd(int x, int y) { return y == 0 ? x : gcdd(y, x % y); }
struct Segment {
    struct node {
        int l, r, gc;
        node* ch[2];
        node(int l, int r, int gc) : l(l), r(r), gc(gc) {}
        inline int mid() { return (l + r) >> 1; }
        inline void up() { gc = gcdd(ch[0]->gc, ch[1]->gc); }
    } *root;
    void build(node *&o, int l, int r) {
        o = new node (l, r, 0);
        if(l == r) { o->gc = read(); return; }
        build(o->ch[0], l, o->mid());
        build(o->ch[1], o->mid()+1, r);
        o->up();
    }
    int query(node *o, int l, int r) {
        if(l <= o->l && o->r <= r) return o->gc;
        int res = 0;
        if(o->mid() >= l) res = query(o->ch[0], l, r);
        if(o->mid() < r) res = gcdd(res, query(o->ch[1], l, r));
        return res;
    }
} tr;
int main() {
    n = read(); m = read();
    tr.build(tr.root, 1, n);
    for(int i = 1, l, r; i <= m; ++ i) {
        l = read(); r =  read();
        printf("%d\n", tr.query(tr.root, l, r));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Paranoid-LS/p/11582701.html

时间: 2024-10-31 09:24:34

P1890 gcd区间 线段树的相关文章

Poj3225Help with Intervals区间线段树

这题不说了,都是泪.这题也是拆点. #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include <list> #include <set&g

洛谷P1890 gcd区间 [2017年6月计划 数论09]

P1890 gcd区间 题目描述 给定一行n个正整数a[1]..a[n]. m次询问,每次询问给定一个区间[L,R],输出a[L]..a[R]的最大公因数. 输入输出格式 输入格式: 第一行两个整数n,m. 第二行n个整数表示a[1]..a[n]. 以下m行,每行2个整数表示询问区间的左右端点. 保证输入数据合法. 输出格式: 共m行,每行表示一个询问的答案. 输入输出样例 输入样例#1: 5 3 4 12 3 6 7 1 3 2 3 5 5 输出样例#1: 1 3 7 说明 对于30%的数据,

【BZOJ3110】【Zjoi2013】K大数查询 树套树 权值线段树套区间线段树

#include <stdio.h> int main() { puts("转载请注明出处谢谢"); puts("http://blog.csdn.net/vmurder/article/details/43020009"); } 题解: 外层权值线段树,内层区间线段树可解. 权值都是1~n,就不用离散化了. 我写了标记永久化. 其它心得神马的: 天生对树形数据结构无爱. 第一次写树套树,终于知道是怎么回事了. (只针对本题) 就是外层每个点都表示了一段

BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

题目大意:有一些位置,这些位置上可以放若干个数字.现在有两种操作. 1.在区间l到r上添加一个数字x 2.求出l到r上的第k大的数字是什么 思路:这种题一看就是树套树,关键是怎么套,怎么写.(话说我也不会来着..)最容易想到的方法就是区间线段树套一个权值线段树,但是区间线段树上的标记就会变得异常复杂.所以我们就反过来套,用权值线段树套区间线段树.这样修改操作在外线段树上就变成了单点修改,外线段树就不用维护标记了.在里面的区间线段树上维护标记就容易多了.具体实现见代码. CODE: #includ

洛谷——P1890 gcd区间

P1890 gcd区间 题目描述 给定一行n个正整数a[1]..a[n]. m次询问,每次询问给定一个区间[L,R],输出a[L]..a[R]的最大公因数. 输入输出格式 输入格式: 第一行两个整数n,m. 第二行n个整数表示a[1]..a[n]. 以下m行,每行2个整数表示询问区间的左右端点. 保证输入数据合法. 输出格式: 共m行,每行表示一个询问的答案. 输入输出样例 输入样例#1: 复制 5 3 4 12 3 6 7 1 3 2 3 5 5 输出样例#1: 复制 1 3 7 说明 对于3

【hdu5381】维护区间内所有子区间的gcd之和-线段树

题意:给定n个数,m个询问,每次询问一个区间内所有连续子区间的gcd的和.n,m<=10^5 题解: 这题和之前比赛的一题很像.我们从小到大枚举r,固定右端点枚举左端点,维护的区间最多只有log段.为什么?以为长区间的gcd肯定是短区间gcd的约数,并且要是不同的话至少要/2,最多那就只有log数值这么多段.还有,相同gcd的区间一定是连续的若干个(想想gcd是怎么求的就知道了).线段树每个端点x维护的是以x为左端点,r从1到当前的r的gcd的和.链表维护log段数,然后每次加到线段树里更新.

hdu 5381 The sum of gcd(线段树等差数列区间修改+单点查询)

题意: 给出一个数组a,叫你每次询问如下等式的值. f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj) 解析: 思考了很久终于理解了学长的思路 给你一个序列,这个序列的子序列gcd的个数不会超过logN个(N为每个数字,最大能取到的范围) 因为求gcd是递减的,每次至少除以2,所以gcd的个数只会有logN个. 然后让我们来看看题目要求的是什么. 所有子区间的gcd的和. 比如[1, 5]这个区间可以分解成如下子区间. [1, 1] [1, 2] [1, 3] [1, 4]

hdu 5381 The sum of gcd(线段树+gcd)

题目链接:hdu 5381 The sum of gcd 将查询离线处理,按照r排序,然后从左向右处理每个A[i],碰到查询时处理.用线段树维护,每个节点表示从[l,i]中以l为起始的区间gcd总和.所以每次修改时需要处理[1,i-1]与i的gcd值,但是因为gcd值是递减的,成log级,对于每个gcd值记录其区间即可.然后用线段树段修改,但是是修改一个等差数列. #include <cstdio> #include <cstring> #include <vector>

gcd(线段树离线处理)——HDU 4630

对应HDU题目:点击打开链接 No Pain No Game Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 1801    Accepted Submission(s): 770 Problem Description Life is a game,and you lose it,so you suicide. But you can