维护gcd的线段树 补发一波。。。

基础线段树(辣鸡的不行)



发现自己线段树除了会维护加法和乘法就啥也不会了QWQ太菜了

瞎写了一个维护gcd的

首先,gcd(x,y)= gcd(x,y-x) 并且很容易推广到n个数,所以我们可以把原数组差分一下,

find时就左右子树大力合并gcd,最后和左端点元素本身取gcd;

upd时就直接修改差分数组的端点,同时用树状数组维护原数组变化量;轻松加愉悦。

#include<cstdio>
#include<iostream>
#include<cmath>
#define ll long long
#define R register ll
#define ls tr<<1
#define rs tr<<1|1
using namespace std;
const int N=500050;
ll w[N<<2],c[N],a[N],n,m;
inline ll g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==‘-‘?-1:fix;
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
inline ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
inline void build(int tr,int l,int r) {
    if(l==r) {w[tr]=a[l]-a[l-1]; return;}
    R md=(l+r)>>1;
    build(ls,l,md),build(rs,md+1,r);
    w[tr]=gcd(w[ls],w[rs]);
}
inline ll find(int tr,int l,int r,int LL,int RR) {
    if(l==LL&&r==RR) return abs(w[tr]);
    R md=(l+r)>>1;
    if(RR<=md) return find(ls,l,md,LL,RR);
    else if(LL>md) return find(rs,md+1,r,LL,RR);
    else return abs(gcd(find(ls,l,md,LL,md),find(rs,md+1,r,md+1,RR)));
}
inline void upd(int tr,int l,int r,int pos,ll inc) {
    if(l==r) {w[tr]+=inc; return ;}
    R md=(l+r)>>1;
    if(pos<=md) upd(ls,l,md,pos,inc);
    else upd(rs,md+1,r,pos,inc);
    w[tr]=gcd(w[ls],w[rs]);
}
inline int lbt(int x) {return x&-x;}
inline ll ask(int pos) {R ret=0; for(;pos;pos-=lbt(pos)) ret+=c[pos]; return ret;}
inline void add(int pos,ll inc) {for(;pos<=n;pos+=lbt(pos)) c[pos]+=inc;}
signed main() {
    n=g(),m=g();
    for(R i=1;i<=n;++i) a[i]=g();
    build(1,1,n);
    while(m--) { register char ch;
        while(!isalpha(ch=getchar())); register int l=g(),r=g(); R inc;
        if(ch==‘Q‘) printf("%lld\n",gcd(a[l]+ask(l),find(1,1,n,l+1,r)));
        else {
            inc=g();add(l,inc);upd(1,1,n,l,inc),add(r+1,-inc);
            if(r<n) upd(1,1,n,r+1,-inc);
        }
    }
}


2019.04.07

原文地址:https://www.cnblogs.com/Jackpei/p/10668114.html

时间: 2024-10-11 10:23:04

维护gcd的线段树 补发一波。。。的相关文章

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

bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改! 于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解-- 经过几乎两个小时的调试,终于1A. 需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回.比如说这道题的change和query操作的最后一句话: sum:=f(g[k<<1]+g[k<<1+1]) 而不是 sum:=f(t[k<&

【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(线段树+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>

hdu5381(2015多校8)--The sum of gcd(线段树)

题目链接:点击打开链接 题目大意:f(l,r)=∑ri=l∑rj=igcd(ai,ai+1....aj),给出初始的n个值,q次询问,每次询问输出f(l,r)的值 大多都是说莫队算法,没有想出肿么用,,,,本题用两个线段树完成 首先对于任意一个a[i],每次gcd减小至少一半,所以它向后的gcd最多下降log(a[i])次,可以求出对于每一个a[i]来说的gcd相同的各个区间. 用线段树维护一段区间的gcd,可以查询一段[l,r]的gcd的值x,从i开始枚举左边界l,然后用二分查找到gcd相同的

codeforces316E3 Summer Homework(线段树,斐波那契数列)

题目大意 给定一个n个数的数列,m个操作,有三种操作: \(1\ x\ v\) 将\(a_x\)的值修改成v \(2\ l\ r\\) 求 \(\sum_{i=l}^r x_i*f_{i-l}\) 其中对于\(f\)数组 \(f_0=1\ ,f_1=1\ ,f_i=f_{i-1}+f_{i-2}\) (就是斐波那契数列) \(3\ l\ r\ x\\) 让\(a_i+x,i\in[l,r]\) 其中\(n\le 100000,m\le 100000\) 一看这个题QwQ,就知道是线段树题 QwQ

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 说

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]

bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列