题目背景

题目描述

给定一长度为n的动态序列,请编写一种数据结构,要求支持m次操作,包括查询序列中一闭区间中所有数的GCD,与对一闭区间中所有数加上或减去一个值。

输入输出格式

输入格式:

第1行两个数n,m,表示序列长度和操作次数。

第2行n个数ai,表示给定序列。

第3行至第m+2行,每行3~4个数:

(1) 1 x y k 表示将[x,y]上的所有数加上k。

(2) 2 x y 表示询问[x,y]上所有数的GCD。

输出格式:

对所有操作2,输出一个数,表示询问结果。

输入输出样例

输入样例#1:

7 3
4 8 2 6 5 7 10
2 1 4
1 2 3 7
2 2 3
输出样例#1:

2
3

说明

定义:a,b∈Z时,gcd(a,b)=gcd(abs(a),abs(b))

对于30%的数据,n,m<=1000。

对于90%的数据,n,m<=100000。

对于100%的数据,n,m<=200000,ai<=1e7(初始),abs(k)<=1e7。

题解:

如果题目要求改为只支持区间查询,那么线段树或ST表都可以很方便地实现。进一步思考,区间修改无法用普通线段树实现的根本原因在于对[l,r]修改后[l,r]的结果无法O(1)计算出来。
如果区间修改改为单点修改,则可以用线段树暴力log(n)修改。

此处证明一个引理:gcd(a1,a2,a3,...,ai)=gcd(a1,a2-a1,a3-a2,...ai-ai-1).
设S为ai的公因数集合,T为ai-ai-1的公因数集合
设p为ai的任意一个公因数,则有p|ai,由整除的性质知p|ai-ai-1,则p一定是ai-ai-1的公因数,所以S是T的子集。
同理,设q为ai-ai-1的任意一个公因数,运用同样的性质可知q一定是ai的公因数,所以T是S的子集。
综上,S=T,所以max{S}=max{T},即gcd(a1,a2,a3,...,ai)=gcd(a1,a2-a1,a3-a2,...ai-ai-1).

所以我们将原数组a进行差分,设差分后数组为d,区间查询[l,r]则转化为gcd(gcd(d[l+1,r]),a[l]);差分后区间修改变为单点修改,可用线段树暴力实现。

具体操作:将原数组进行差分,用一棵支持单点修改的线段树维护gcd,将差分数组用一个树状数组维护前缀和(用来求出变化后的a[l],也可以合并在线段树中)。
注意:差分时对区间[l,r]涉及到对r+1的操作,为防止溢出,线段树区间增大至[1,n+1]。

代码如下:

#include<bits/stdc++.h>
#define LL long long
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=2e5+10;
LL node[4*maxn],a[maxn],c[maxn],d[maxn];
int n,m;LL ans;
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
void pushup(int x){node[x]=abs(gcd(node[x<<1],node[x<<1|1]));}
void build(int x,int l,int r)
{
    if(l==r){node[x]=d[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    pushup(x);
}
void change(int x,int l,int r,int pos,int d)
{
    if(l==r){node[x]+=d;return;}
    int mid=(l+r)>>1;
    if(pos<=mid){change(x<<1,l,mid,pos,d);}
    else{change(x<<1|1,mid+1,r,pos,d);}
    pushup(x);
}
void query(int x,int l,int r,int sj,int tj)
{
    if(sj<=l&&r<=tj){ans=abs(gcd(node[x],ans));return;}
    int mid=(l+r)>>1;
    if(sj<=mid){query(x<<1,l,mid,sj,tj);}
    if(mid+1<=tj){query(x<<1|1,mid+1,r,sj,tj);}
    pushup(x);
}
void add(int x,int d)
{
    int i;
    for(i=x;i<=n;i+=lowbit(i)){c[i]+=d;}
}
LL sum(int x)
{
    int i;LL ans=0;
    for(i=x;i>=1;i-=lowbit(i)){ans+=c[i];}
    return ans;
}
int main()
{
    int i,j,flag,l,r,dlt;
    cin>>n>>m;
    for(i=1;i<=n;i++){scanf("%lld",&a[i]);}
    n++;
    for(i=1;i<=n;i++){d[i]=a[i]-a[i-1];add(i,d[i]);}
    build(1,1,n);
    //for(i=1;i<=3*n;i++){printf("i=%d node[i]=%d\n",i,node[i]);}
    for(i=1;i<=m;i++)
    {
        scanf("%d%d%d",&flag,&l,&r);
        if(flag==1){scanf("%d",&dlt);change(1,1,n,l,dlt);change(1,1,n,r+1,-dlt);add(l,dlt);add(r+1,-dlt);}
        else{ans=0;query(1,1,n,l+1,r);/*printf("ans=%d sum(l)=%d\n",ans,sum(l));*/printf("%lld\n",abs(gcd(ans,sum(l))));}
    }
    return 0;
}

Luogu T9376 区间GCD的更多相关文章

  1. HDU 5726 GCD 区间GCD=k的个数

    GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submis ...

  2. Codeforces 914D - Bash and a Tough Math Puzzle 线段树,区间GCD

    题意: 两个操作, 单点修改 询问一段区间是否能在至多一次修改后,使得区间$GCD$等于$X$ 题解: 正确思路; 线段树维护区间$GCD$,查询$GCD$的时候记录一共访问了多少个$GCD$不被X整 ...

  3. bzoj 5028: 小Z的加油店——带修改的区间gcd

    Description 小Z经营一家加油店.小Z加油的方式非常奇怪.他有一排瓶子,每个瓶子有一个容量vi.每次别人来加油,他会让 别人选连续一段的瓶子.他可以用这些瓶子装汽油,但他只有三种操作: 1. ...

  4. dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)

    1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total ...

  5. HDU5381【莫队算法+区间GCD特性】

    前言: 主要最近在刷莫队的题,这题GCD的特性让我对莫队的使用也有了新的想法.给福利:神犇的一套莫队算法题 先撇开题目,光说裸的一个莫队算法,主要的复杂度就是n*sqrt(n)对吧,这里我忽略了一个左 ...

  6. 区间 GCD

    区间 GCD题目描述最近 JC 同学刚学会 gcd,于是迷上了与 gcd 有关的问题.今天他又出了一道这样的题目,想要考考你,你能顺利完成吗?给定一个长度为 n 的字符串 s[1..n],串仅包含小写 ...

  7. HDU 5726 GCD (2016多校、二分、ST表处理区间GCD、数学)

    题目链接 题意 : 给出一个有 N 个数字的整数数列.给出 Q 个问询.每次问询给出一个区间.用 ( L.R ) 表示.要你统计这个整数数列所有的子区间中有多少个和 GCD( L ~ R ) 相等.输 ...

  8. 区间gcd

    http://codeforces.com/problemset/problem/914/D 题意:给你n个数,两种操作:1.询问区间[l,r]在至多一次修改一个数的条件下区间gcd是否等于x. 2. ...

  9. 区间加值,区间gcd, 牛客949H

    牛客小白月赛16H 小阳的贝壳 题目链接 题意 维护一个数组,支持以下操作: 1: 区间加值 2: 询问区间相邻数差的绝对值的最大值 3: 询问区间gcd 题解 设原数组为\(a\), 用线段树维护\ ...

随机推荐

  1. 这么优雅的Java ORM没见过吧!

      Java的ORM框架有很多,但由于Java语言的限制大部分都不够优雅也不够简单,所以作者只能另辟蹊径造轮子了.照旧先看示例代码了解个大概,然后再解释实现原理. 一.ORM示例 1. Insert ...

  2. go跳出多层循环的几种方式

    前言 比如这样的需求, 遍历一个 切片, 切片内容是切片1, 需求是判断切片1中某个是否有相应数据, 有就返回 正文 我们需要考虑的是在写两层遍历时如何在获取结果后结束这两层遍历 变量法 设置一个变量 ...

  3. Spring Security OAuth2.0认证授权五:用户信息扩展到jwt

    历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...

  4. Python作业---内置数据类型

    实验2 内置数据类型 实验性质:验证性 一.实验目的 1.掌握内置函数.列表.切片.元组的基本操作: 2.掌握字典.集合和列表表达式的基本操作. 二.实验预备知识 1.掌握Python内置函数的基/本 ...

  5. Logrotate工具使用

    Logrotate ​ logrotate是一个被设计来简化系统管理日志文件的工具,在系统运行时,如果产生大量的日志文件,可以使用该工具进行管理,如/var/log/*文件夹是存储系统和应用日志的目录 ...

  6. 日常采坑:.NetCore上传大文件

    一..NetCore上传大文件 .NetCore3.1 webapi 本地测试上传时,遇到一个坑,大点的文件直接失败,根本不走控制器方法. 二.大文件上传配置 IFormFile方式,vs IIS E ...

  7. Assuming that agent dropped connection because of access permission

    Assuming that agent dropped connection because of access permission

  8. --safe-user-create

    此参数如果启用,用户将不能用grant语句创建新用户,除非用户有mysql数据库中user表的insert权限, ./mysqld_safe  --safe-user-create & 用-- ...

  9. 安装percona-toolkit

    http://www.percona.com/software/percona-toolkityum -y install perl-DBIyum -y install perl-DBD-mysqly ...

  10. oracle 释放表空间到OS(resize)

    1.查看表空间里面的对象 SELECT OWNER AS OWNER, SEGMENT_NAME AS SEGMENT_NAME, SEGMENT_TYPE AS SEGMENT_TYPE, SUM ...