首页 > 其他分享 >区间最大公约数

区间最大公约数

时间:2022-11-15 14:44:33浏览次数:81  
标签:gcd int LL 最大公约数 区间 ldots

区间最大公约数

给定一个长度为 $N$ 的数列 $A$,以及 $M$ 条指令,每条指令可能是以下两种之一:

  1. C l r d ,表示把 $A[l],A[l+1], \ldots ,A[r]$ 都加上 $d$。
  2. Q l r ,表示询问 $A[l],A[l+1], \ldots ,A[r]$ 的最大公约数(GCD)。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 $N,M$。

第二行 $N$ 个整数 $A[i]$。

接下来 $M$ 行表示 $M$ 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

$N \leq 500000,M \leq 100000$,
$1 \leq A[i] \leq {10}^{18}$,
$|d| \leq {10}^{18}$

输入样例:

5 5
1 3 5 7 9
Q 1 5
C 1 5 1
Q 1 5
C 3 3 6
Q 2 4

输出样例:

1
2
4

 

解题思路

  如果没有修改操作只有查询操作,那么直接维护区间的最大公约数就可以了。如果只有单点修改,一样只需要维护区间的最大公约数就可以了。如果是区间修改那么只维护区间最大公约数就不可以了,因为如果给某个区间都加上$x$,那么可以发现$\gcd{(a, b, c)}$与$\gcd{(a+x, b+x, c+x)}$没有什么关系,只根据之前的最大公约数是求不出来加上某个数后的最大公约数的。

  最大公约数有这样一个性质:$$\gcd{(a_1, a_2, \ldots, a_n)} = \gcd{(a_1, a_2 - a_1, \ldots, a_n - a_{n-1})}$$

  这里简单证明一下,根据性质$\gcd{(a, b)} = \gcd{(b, a)}$,$\gcd{(a, b)} = \gcd{(a, b - a)}$,$\gcd{(a, b, c)} = \gcd{(\gcd{(a, b)}, c)}$,有$$\begin{align*} \gcd{(a, b, c, d)} &= \gcd{(a, b - a, c, d)} \\ &= \gcd{(a, b - a, c - b + a, d)} \\  &= \gcd{(a, b - a, c - b, d)} \\ &= \gcd{(a, b - a, c - b, d - c + b)} \\ &= \gcd{(a, b - a, c - b, d - c + a)} \\ &= \gcd{(a, b - a, c - b, d - c)} \end{align*}$$

  可以归纳证明多个变量的情况。

  因此可以用线段树来维护一个差分序列,这样区间修改就可以变成单点修改了,在维护区间最大公约数的同时再维护一个区间和。当查询区间$[l, r]$,就是求$\gcd{(a_{l}, a_{l+1} - a_{l}, \ldots, a_{r} - a_{r - 1})}$,$a_{l}$可以用维护的区间和得到,$\gcd{(a_{l+1} - a_{l}, \ldots, a_{r} - a_{r - 1})}$可以用维护的区间最大公约数得到。

  AC代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 const int N = 5e5 + 10;
 7 
 8 LL a[N];
 9 struct Node {
10     int l, r;
11     LL s, d;
12 }tr[N * 4];
13 
14 LL gcd(LL a, LL b) {
15     return b ? gcd(b, a % b) : a;
16 }
17 
18 void build(int u, int l, int r) {
19     if (l == r) {
20         tr[u] = {l, r, a[l] - a[l - 1], a[l] - a[l - 1]};   // 维护的是差分序列
21     }
22     else {
23         int mid = l + r >> 1;
24         build(u << 1, l, mid);
25         build(u << 1 | 1, mid + 1, r);
26         tr[u] = {l, r, tr[u << 1].s + tr[u << 1 | 1].s, gcd(tr[u << 1].d, tr[u << 1 | 1].d)};
27     }
28 }
29 
30 void modify(int u, int x, LL c) {
31     if (tr[u].l == x && tr[u].r == x) {
32         tr[u].s += c, tr[u].d += c;
33     }
34     else {
35         int mid = tr[u].l + tr[u].r >> 1;
36         if (x <= mid) modify(u << 1, x, c);
37         else modify(u << 1 | 1, x, c);
38         tr[u].s = tr[u << 1].s + tr[u << 1 | 1].s;
39         tr[u].d = gcd(tr[u << 1].d, tr[u << 1 | 1].d);
40     }
41 }
42 
43 Node query(int u, int l, int r) {
44     if (tr[u].l >= l && tr[u].r <= r) return tr[u];
45     int mid = tr[u].l + tr[u].r >> 1;
46     if (r <= mid) {
47         return query(u << 1, l, r);
48     }
49     else if (l >= mid + 1) {
50         return query(u << 1 | 1, l, r);
51     }
52     else {
53         Node t1 = query(u << 1, l, r), t2 = query(u << 1 | 1, l, r);
54         return {l, r, t1.s + t2.s, gcd(t1.d, t2.d)};
55     }
56 }
57 
58 int main() {
59     int n, m;
60     scanf("%d %d", &n, &m);
61     for (int i = 1; i <= n; i++) {
62         scanf("%lld", a + i);
63     }
64     build(1, 1, n + 1);
65     while (m--) {
66         char op[5];
67         int l, r;
68         scanf("%s %d %d", op, &l, &r);
69         if (op[0] == 'C') {
70             LL c;
71             scanf("%lld", &c);
72             modify(1, l, c), modify(1, r + 1, -c);
73         }
74         else {
75             LL d = abs(query(1, 1, l).s);   // a[l]通过差分序列的前缀和求得
76             if (l + 1 <= r) printf("%lld\n", abs(gcd(d, query(1, l + 1, r).d)));    // 当l+1 <= r才存在剩余部分的最大公约数
77             else printf("%lld\n", d);
78         }
79     }
80     
81     return 0;
82 }

 

参考资料

  AcWing 246. 区间最大公约数(算法提高课):https://www.acwing.com/video/650/

标签:gcd,int,LL,最大公约数,区间,ldots
From: https://www.cnblogs.com/onlyblues/p/16892364.html

相关文章

  • 最大公约数、最小公倍数的求解
    1#include<stdio.h>2intmain()3{4intr,m,n;5printf("请输入m,n(用逗号间隔):");6scanf("%d,%d",&m,&n);7r=m%n;8while(r!......
  • 合并区间,对象比较大小,int compare(T a, T b)返回值位负数,则认为a小,否则a大
     import java.util.*;/** * Definition for an interval. * public class Interval { *     int start; *     int end; *     ......
  • P1063 能量项链 区间DP
    题干 AC记录我原本的dfs的转移式子就只有将两边的单个拎出来,将其余的大基团合并也就是这两个情况: 和但是忽视了类似这种的情况:也就是说,我没有讨论两个大基团合并......
  • 洛谷P4168 蒲公英 分块处理区间众数模板
    题面。许久以前我还不怎么去机房的时候,一位大佬好像一直在做这道题,他称这道题目为“大分块”。其实这道题目的思想不只可以用于处理区间众数,还可以处理很多区间数值相关......
  • 区间dp
    区间dp:顾名思义,就是从小的区间推向大的区间。区间dp的一般模板:for(intlen=1;len<=n;len++){for(intj=1;j+len<=n+1;j++){intr=j+len-1;......
  • 最大公约数 C/C++ leetcode , 辗转相除,更相减损
    #include <iostream>using namespace std;// 辗转相除法求最大公约数,用大的模小的,然后用除数模余数,该接口在新版的C++17的numeric 包中也有int gcd1(int a ,......
  • 线段树维护区间历史版本和
    好久没写博客了,主要是这玩意网上有点难搜到,就干脆糊了一个另外区间加操作的题见这个博客给定长为\(n\)的01序列,\(m\)个询问,第\(i\)次认为产生一个新版本\(i\);要......
  • 区间和的个数
    packageclass05;/***区间和的个数*<p>*https://leetcode.com/problems/count-of-range-sum/*给你一个整数数组nums以及两个整数lower和upper。求数......
  • 每日一题-区间合并
    MergingIntervalssort(a.begin(),a.end());vector<pair<int,int>>res;for(inti=0;i<n;++i){intl=0,r=res.size()-1;while(l<r......
  • LeetCode 763. 划分字母区间
    1、一上来先遍历数组,找到每个字母最后出现的位置。2、再次遍历数组,保持一个last,表示当前至少应该在哪里分割classSolution{public:vector<int>partitionLabel......