D - 小Z的加油店

小Z经营一家加油店。小Z加油的方式非常奇怪。他有一排瓶子,每个瓶子有一个容量vi。每次别人来加油,他会让

别人选连续一段的瓶子。他可以用这些瓶子装汽油,但他只有三种操作:

1.把一个瓶子完全加满;

2.把一个瓶子完全倒空;

3.把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了。

当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x。

为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少?

当然他不会一点汽油都不给客户。

Input

第一行包括两个数字:瓶子数n,事件数m。

第二行包含n个整数,表示每个瓶子的容量vi。

接下来m行,每行先有三个整数fi li ri。

若fi=1表示询问li到ri他最少能倒腾出的汽油量最少是多少?

若fi=2 再读入一个整数x。表示他将li到ri的瓶子容量都增加了x。

1 <= n,m <= 10^5 , 1<=li<=ri<=n , 1<=初始容量,增加的容量<=1000

Output

对于每个询问输出对应的答案

Sample Input3 4 2 3 4 1 1 3 2 2 2 1 1 1 3 1 2 3

Sample Output1 2 4Hint

有可能出现L>R,读入的p不只有0和1,把所有非1操作当成2才能AC

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

推荐一个差分的博客: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还是很难找的。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <vector>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 10;
struct node
{
    int l, r;
    int sum, val;
}tree[maxn*4];
int a[maxn];

int gcd(int a,int b)
{
    return b == 0 ? a : gcd(b, a%b);
}

void push_up(int id)
{
    tree[id].sum = tree[id << 1].sum + tree[id << 1 | 1].sum;
    tree[id].val = gcd(tree[id << 1].val, tree[id << 1 | 1].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) >> 1;
    build(id << 1, l, mid);
    build(id << 1 | 1, mid + 1, 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)>>1, ans = 0;
    if (l <= mid) ans += query_sum(id << 1, l, r);
    if(r>mid) ans += query_sum(id << 1 | 1, 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) >> 1, ans = 0;
    if (l <= mid) ans = gcd(ans, query_val(id << 1, l, r));
    if (r > mid) ans = gcd(ans, query_val(id << 1 | 1, 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) >> 1;
    if (p <= mid) update(id << 1, p, x);
    else update(id << 1 | 1, p, x);
    push_up(id);
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = n; i >= 1; i--) a[i] = a[i] - a[i - 1];//要从后往前,注意差分是每一个真实值和这个真实值之前的真实值进行差分
    build(1, 1, n);
    for (int i = 1; i <= m; i++)
    {
        int p, l, r;
        scanf("%d%d%d", &p, &l, &r);
        if (l > r) swap(l, r);//这个其实交换不交换都差不多
        if (p == 1)
        {
            int exa1 = query_sum(1, 1, l);//去查找第i个数的真实值
            int exa2 = query_val(1, l + 1, r);//i到r之间的gcd
            int ans = abs(gcd(exa1,exa2));//因为是进行差分了,所以gcd之后也有可能有负值
            printf("%d\n", ans);
        }
        else
        {
            int x;
            scanf("%d", &x);
            update(1, l, x);//差分的更新
            update(1, r + 1, -x);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/EchoZQN/p/10821048.html

时间: 2024-08-13 01:11:30

D - 小Z的加油店的相关文章

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

Description 小Z经营一家加油店.小Z加油的方式非常奇怪.他有一排瓶子,每个瓶子有一个容量vi.每次别人来加油,他会让 别人选连续一段的瓶子.他可以用这些瓶子装汽油,但他只有三种操作: 1.把一个瓶子完全加满: 2.把一个瓶子完全倒空: 3.把一个瓶子里的汽油倒进另一个瓶子,直到倒出瓶子空了或者倒进的瓶子满了. 当然,为了回馈用户,小Z会时不时选择连续一段瓶子,给每个瓶子容积都增加x. 为了尽可能给更多的人加油,每次客户来加油他都想知道他能够倒腾出的汽油量最少是多少? 当然他不会一点汽

bzoj 2038 小Z的袜子(hose)(莫队算法)

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 11542  Solved: 5166[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命--具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

[国家集训队2010]小Z的袜子

★★★   输入文件:hose.in   输出文件:hose.out   简单对比 时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便

BZOJ 2038: [2009国家集训队]小Z的袜子(hose)

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 7676  Solved: 3509[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只

清橙A1206 小Z的袜子(莫队算法)

A1206. 小Z的袜子 时间限制:1.0s   内存限制:512.0MB 总提交次数:744   AC次数:210   平均分:44.44 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2010中国国家集训队命题答辩 问题描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是

【BZOJ】2038: [2009国家集训队]小Z的袜子(hose)

[算法]莫队 [题解] BZOJ 2038 2009国家集训队 小Z的袜子(hose) 莫队算法 莫队--讲稿? 施工中--

【洛谷T4896】小Z爱划水

题目描述 小Z和其它机房同学都面临一个艰难的抉择,那就是 要不要划水? 每个人都有自己的一个意见,有的人想做题,有的人想划水. 当然,每个人只能选择一个事情做.如果一个人做的事情和他想做的不同,那么他会产生1不满意度. 更棘手的是,他们之间一些人是朋友,如果两人是朋友,但是他们做的事情不同,那么会有1不满意度产生. 小Z不想看到大家闹得不高兴,他想知道,不满意度最小能是多少? 输入输出格式 输入格式: 第一行两个数字n,m 分别表示有n个人和m对朋友关系 第二行n个0/1,1表示想做题,0表示想

【BZOJ 4031】 4031: [HEOI2015]小Z的房间 (Matrix-Tree Theorem)

4031: [HEOI2015]小Z的房间 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1089  Solved: 533 Description 你突然有了一个大房子,房子里面有一些房间.事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子.在一开始的时候,相邻的格子之间都有墙隔着. 你想要打通一些相邻房间的墙,使得所有房间能够互相到达.在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙).同

BZOJ 2038 小z的袜子 &amp; 莫队算法(不就是个暴力么..)

题意: 给一段序列,询问一个区间,求出区间中.....woc! 贴原题! 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概