这个题目是一个线段树+差分+GCD

推荐一个差分的博客:https://www.cnblogs.com/cjoierljl/p/8728110.html

学会了这个简单差分之后,就可以把这个题目的区间更新转化成单点更新了,emmm...可能还是不太理解,那就说下具体思路吧。

这个题目一看感觉很难,然后就取看题解,这个看了题解之后发现裴蜀定理+线段树。

讲一下为什么是裴蜀定理+线段树,线段树应该没有什么异议,因为这个有区间更新区间查询,而且n,m的数值很大,不用线段树很容易T。

因为你要进行很多次操作,就可以列一个式子:ax1+bx2+cx3....=ans

这个式子,如果你的数论学的很好的话,就可以知道应该用裴蜀定理,这个裴蜀定理可以自己简单学习一下。

根据裴蜀定理这个最小值应该是gcd(a,b,c,d....),所以我们就应该用线段树来求一个区间的gcd。

为什么又要用到差分呢?

因为如果你每次进行区间更新都是每一个点进行更新(只能这样,不然就无法求gcd),这样子的话,你就会发现T了。

所以我们不这么写,从刚刚的那个博客可以看出,这个可以把区间更新转化成单点更新了,就只需要更新区间左端点和区间右端点+1。

所以总结一下,这个题解法就是:

先对序列进行差分,然后用差分数列进行建树,这个要记录一个和sum和gcd val

然后就是查询,这个查询就是先查左端点的sum,然后再查左端点到右端点之间的差分的val(这个是根据裴蜀定理得出的)

然后就是更新,这个更新是只要对左端点和右端点+1进行更新就可以了。

之前是写之前的思路,接下来说说写的过程种碰到的bug,写在代码里了。

其实和普通线段树是差不多的,但是就是会有很多小bug没注意到。

线段树的bug还是很难找的。

写一下我对于差分的理解:
如果你碰到你要更新一个区间,给这个区间上的每一个数都加上一个x,让你求n次操作之后求一个区间总和。
又有时间限制,不可以一个一个的去加,这个时候就可以用上差分了。
就是先将这个数列进行差分得到一个差分数列,然后就是对于这个区间的第一位+x,
最后一位的后面一位-x。
这样子操作n次之后再用前缀和求出原来的数列,最后原来的数列再运算即可。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + ;
struct node
{
int l, r;
int sum, val;
}tree[maxn*];
int a[maxn]; int gcd(int a,int b)
{
return b == ? a : gcd(b, a%b);
} void push_up(int id)
{
tree[id].sum = tree[id << ].sum + tree[id << | ].sum;
tree[id].val = gcd(tree[id << ].val, tree[id << | ].val);
} void build(int id,int l,int r)//建树
{
tree[id].l = l;
tree[id].r = r;
if(tree[id].l==tree[id].r)
{
tree[id].sum = tree[id].val = a[l];
return;
}
int mid = (l + r) >> ;
build(id << , l, mid);
build(id << | , mid + , r);
push_up(id);
} int query_sum(int id,int l,int r)//求到第区间第一个的真实值
{
if(l<=tree[id].l&&r>=tree[id].r)
{
return tree[id].sum;
}
int mid = (tree[id].l + tree[id].r)>>, ans = ;
if (l <= mid) ans += query_sum(id << , l, r);
if(r>mid) ans += query_sum(id << | , l, r);
return ans;
} int query_val(int id,int l,int r)//求区间除开第一个的差分gcd
{
if(l<=tree[id].l&&r>=tree[id].r)
{
return tree[id].val;
}
int mid = (tree[id].l + tree[id].r) >> , ans = ;
if (l <= mid) ans = gcd(ans, query_val(id << , l, r));
if (r > mid) ans = gcd(ans, query_val(id << | , l, r));
return ans;
} void update(int id,int p,int x)
{
if (p > tree[id].r) return;
if(tree[id].l==tree[id].r)
{
tree[id].sum += x;
tree[id].val += x;
return;
}
int mid = (tree[id].l + tree[id].r) >> ;
if (p <= mid) update(id << , p, x);
else update(id << | , p, x);
push_up(id);
} int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = ; i <= n; i++) scanf("%d", &a[i]);
for (int i = n; i >= ; i--) a[i] = a[i] - a[i - ];//要从后往前,注意差分是每一个真实值和这个真实值之前的真实值进行差分
build(, , n);
for (int i = ; i <= m; i++)
{
int p, l, r;
scanf("%d%d%d", &p, &l, &r);
if (l > r) swap(l, r);//这个其实交换不交换都差不多
if (p == )
{
int exa1 = query_sum(, , l);//去查找第i个数的真实值
int exa2 = query_val(, l + , r);//i到r之间的gcd
int ans = abs(gcd(exa1,exa2));//因为是进行差分了,所以gcd之后也有可能有负值
printf("%d\n", ans);
}
else
{
int x;
scanf("%d", &x);
update(, l, x);//差分的更新
update(, r + , -x);
}
}
return ;
}

D - 小Z的加油店 线段树+差分+GCD的更多相关文章

  1. [BZOJ5028]小Z的加油店

    [BZOJ5028]小Z的加油店 题目大意: 一个长度为\(n(n\le10^5)\)的数列,\(m(m\le10^5)\)次操作,支持区间加和区间\(\gcd\). 思路: 线段树维护差分,\(\g ...

  2. 【BZOJ】5028: 小Z的加油店

    [算法]数学+线段树/树状数组 [题解] 首先三个操作可以理解为更相减损术或者辗转相除法(待证明),所以就是求区间gcd. 这题的问题在线段树维护gcd只能支持修改成一个数,不支持加一个数. 套路:g ...

  3. 5028: 小Z的加油店(线段树)

    NOI2012魔幻棋盘弱化版 gcd(a,b,c,d,e)=gcd(a,b-a,c-b,d-c,e-d) 然后就可以把区间修改变成差分后的点修了. 用BIT维护原序列,线段树维护区间gcd,支持点修区 ...

  4. bzoj5028小Z的加油店(线段树+差分)

    题意:维护支持以下两种操作的序列:1 l r询问a[l...r]的gcd,2 l r x把a[l...r]全部+x 题解:一道经典题.根据gcd(a,b)=gcd(a-b,b)以及区间加可知,这题可以 ...

  5. 【bzoj5028】小Z的加油店 扩展裴蜀定理+差分+线段树

    题目描述 给出 $n$ 个瓶子和无限的水,每个瓶子有一定的容量.每次你可以将一个瓶子装满水,或将A瓶子内的水倒入B瓶子中直到A倒空或B倒满.$m$ 次操作,每次给 $[l,r]$ 内的瓶子容量增加 $ ...

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

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

  7. BZOJ 5028 小Z的加油店

    [题解] 本题要求求出区间内的各个元素通过加减之后能够得出的最小的数,那么根据裴蜀定理可知答案就是区间内各个元素的最大公约数. 那么本题题意化简成了维护一个序列,支持区间加上某个数以及查询区间元素的最 ...

  8. 牛客小白月赛16 H小阳的贝壳 (线段树+差分数组)

    链接:https://ac.nowcoder.com/acm/contest/949/H来源:牛客网 题目描述 小阳手中一共有 n 个贝壳,每个贝壳都有颜色,且初始第 i 个贝壳的颜色为 colico ...

  9. 【BZOJ4031】小Z的房间(矩阵树定理)

    [BZOJ4031]小Z的房间(矩阵树定理) 题面 BZOJ 洛谷 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子 ...

随机推荐

  1. editplus 怎么替换为换行

    到editplus 的搜索 菜单中,选择替换,记住 这边如果是简单的一些 通用字符 替换可以直接替换,如果是一些特殊的字符 那必须选择 替换框左下中间的 “正则表达式”,即把这个“正则表达式” 前边的 ...

  2. ModuleNotFoundError: No module named 'sklearn.cross_validation'

    本文为CSDN博主「不服输的南瓜」的原创文章,遵循 CC 4.0 BY-SA 版权协议 原文链接 ModuleNotFoundError: No module named 'sklearn.cross ...

  3. Mark down 使用总结

    Markdown语法 Markdown是一种纯文本.轻量级的标记语言,通过简单的标记,就可以使文本具有一定的格式,操作简单.使用广泛,常见的比如github上的README.md . Markdown ...

  4. 2007 NOIP T1奖学金

    奖学金(07NOIPT1): [题目描述] 某小学最近得到了一笔赞助,打算拿出其中一部分为学习成绩优秀的前5名学生发奖学金.期末,每个学生都有3门课的成绩:语文.数学.英语.先按总分从高到低排序,如果 ...

  5. mongodb connection refused because too many open connections: 819

    Env Debian 9 # 使用通用二进制方式安装 # mongod --version db version v3.4.21-2.19 git version: 2e0631f5e0d868dd5 ...

  6. JUC并发编程基石AQS之主流程源码解析

    前言 由于AQS的源码太过凝练,而且有很多分支比如取消排队.等待条件等,如果把所有的分支在一篇文章的写完可能会看懵,所以这篇文章主要是从正常流程先走一遍,重点不在取消排队等分支,之后会专门写一篇取消排 ...

  7. JasperReports入门教程(二):中文打印

    JasperReports入门教程(二):中文打印 背景 在上一篇中我们介绍了JasperReport的基本入门,也展示了一个报表.但是我们的示例都是使用的英文,如果我们把需要打印的数据改为中文会怎么 ...

  8. 在Thinkphp中微信公众号JsApi支付

    由于网站使用的微信Native扫码支付,现在公众号需要接入功能,怎么办呢,看这官方文档,参考着demo进行写吧.直接进入正题 进入公众号(服务号)设置--->功能设置--->网页授权域名配 ...

  9. bootstrop登陆页面

    bootstrap做登入注册页面,使用validate做表单验证 技术:bootstrap,font-awesome,jquery-validate: 特点:响应式布局,表单验证(用户两次密码是否相同 ...

  10. MySQL根据业务场景归纳常用SQL语句

    素材表数据:user[{"id":1,"name":"x"},{"id":2,"name":&quo ...