这个题目是一个线段树+差分+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. Java编程最差实践常见问题详细说明(2)转

    Java编程最差实践常见问题详细说明(2)转 2012-12-13 13:57:20|  分类: JAVA |  标签:java  |举报|字号 订阅     反射使用不当  错误的写法: Java代 ...

  2. 基于mui的H5套壳APP开发web框架分享

    前言 创建一个main主页面,只有主页面有头部.尾部,中间内容嵌入iframe内容子页面,如果在当前页面进行跳转操作,也是在iframe中进行跳转,而如果点击尾部按钮切换模块.页面,那就切换ifram ...

  3. Nikto使用方法

    Introduction Nikto是一款开源的(GPL)网站服务器扫描器,使用Perl基于LibWhisker开发.它可以对网站服务器进行全面的多种扫描,包括6400多个潜在危险的文件/CGI,检查 ...

  4. Python 应用领域及学习重点

    笔者认为不管学习什么编程语言,首先要知道:学完之后在未来能做些什么? 本文将浅谈 Python 的应用领域及其在对应领域的学习重点.也仅是介绍了 Python 应用领域的"冰山一角" ...

  5. python实现服务器监控报警消息用微信发送(附代码)

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:NicePython PS:如有需要Python学习资料的小伙伴可以加 ...

  6. PHP本地开发利器:内置Web Server

    PHP 5.4.0起, CLI SAPI 提供了一个内置的Web服务器. 命令:php -S 这个内置的Web服务器主要用于本地开发使用,不可用于线上产品环境. URI请求会被发送到PHP所在的的工作 ...

  7. Laravel路由不生效,除了首页全部404解决方案Nginx环境

    原因: 请求根目录/ (http://www.xxx.com/public/),会请求public/index.php 输入其他路由地址时,会把你的请求定位到:http://www.xxx.com/i ...

  8. 干货!Python中字符串、列表、元祖、字典,集合之间的转换

    一.字符串的转化 1.字符串转换成列表 字符串转换成list 的时候,str可以作为迭代对象,直接放入:也可以使用split对字符串进行切割.然后返回list s = '1a1b1c' print(l ...

  9. CSS常用遮罩层

    为什么80%的码农都做不了架构师?>>>   CSS常用遮罩层 应用场景: 上传了一张图片,鼠标移入到图片上的时候显示遮罩层,并且提示点击删除. 通过改变遮罩层的透明度来实现显示隐藏 ...

  10. 限流 - Guava RateLimiter

    2019独角兽企业重金招聘Python工程师标准>>> 限流 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦并发访问/请求达到限制速率或者 ...