题目描述

给出一个长度为 $n$ 的序列,支持 $m$ 次操作,操作有四种:区间加、区间下取整除、区间求最小值、区间求和。

$n\le 100000$ ,每次加的数在 $[-10^4,10^4]$ 之间,每次除的数在 $[2,10^9]$ 之间。


题解

线段树+均摊分析

【uoj#228】基础数据结构练习题 类似的均摊分析题。

对于原来的两个数 $a$ 和 $b$ ( $a>b$ ) ,原来的差是 $a-b$ ,都除以 $d$ 后的差是 $\frac{a-b}d$ ,相当于差也除了 $d$ 。

而当区间差为 $0$ 或 $a=kd,b=kd-1$ 的 $1$ 时,区间下取整除就变成了区间减。

因此当一个区间下取整除了 $\log(Max-Min)$ 次后就不需要暴力下取整除,直接区间减即可。

定义线段树节点势能为 $\log(Max-Min)$ ,那么每次对 $[l,r]$ 下取整除就是将所有 $l\le x,y\le r$ ,且势能不为 $0$ 的节点 $[x,y]$ 的势能减 $1$ ,代价为势能减少总量。

分析区间加操作:只会修改到经过的节点的势能,影响 $\log$ 个节点,将这些点的势能恢复为 $\log(Max-Min)$ 。

因此总的时间复杂度就是总势能量 $O((n+m\log n)\log a)$ 。

注意C++中的 '/' 并不是下取整,而是向0取整,因此需要按正负分类讨论手写下取整。

#include <cstdio>
#include <algorithm>
#define N 400010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
ll sum[N] , mx[N] , mn[N] , add[N];
inline ll fdiv(ll x , ll y)
{
return x > 0 ? x / y : (x - y + 1) / y;
}
inline void pushup(int x)
{
sum[x] = sum[x << 1] + sum[x << 1 | 1];
mx[x] = max(mx[x << 1] , mx[x << 1 | 1]);
mn[x] = min(mn[x << 1] , mn[x << 1 | 1]);
}
inline void pushdown(int l , int r , int x)
{
if(add[x])
{
int mid = (l + r) >> 1;
sum[x << 1] += add[x] * (mid - l + 1) , sum[x << 1 | 1] += add[x] * (r - mid);
mx[x << 1] += add[x] , mx[x << 1 | 1] += add[x];
mn[x << 1] += add[x] , mn[x << 1 | 1] += add[x];
add[x << 1] += add[x] , add[x << 1 | 1] += add[x];
add[x] = 0;
}
}
void build(int l , int r , int x)
{
if(l == r)
{
scanf("%lld" , &sum[x]) , mx[x] = mn[x] = sum[x];
return;
}
int mid = (l + r) >> 1;
build(lson) , build(rson);
pushup(x);
}
void vadd(int b , int e , ll a , int l , int r , int x)
{
if(b <= l && r <= e)
{
sum[x] += a * (r - l + 1) , mx[x] += a , mn[x] += a , add[x] += a;
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) vadd(b , e , a , lson);
if(e > mid) vadd(b , e , a , rson);
pushup(x);
}
void vdiv(int b , int e , ll d , int l , int r , int x)
{
if(b <= l && r <= e && mx[x] - fdiv(mx[x] , d) == mn[x] - fdiv(mn[x] , d))
{
ll a = mx[x] - fdiv(mx[x] , d);
sum[x] -= a * (r - l + 1) , mx[x] -= a , mn[x] -= a , add[x] -= a;
return;
}
pushdown(l , r , x);
int mid = (l + r) >> 1;
if(b <= mid) vdiv(b , e , d , lson);
if(e > mid) vdiv(b , e , d , rson);
pushup(x);
}
ll qmin(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return mn[x];
pushdown(l , r , x);
int mid = (l + r) >> 1;
ll ans = 1ll << 62;
if(b <= mid) ans = min(ans , qmin(b , e , lson));
if(e > mid) ans = min(ans , qmin(b , e , rson));
return ans;
}
ll qsum(int b , int e , int l , int r , int x)
{
if(b <= l && r <= e) return sum[x];
pushdown(l , r , x);
int mid = (l + r) >> 1;
ll ans = 0;
if(b <= mid) ans += qsum(b , e , lson);
if(e > mid) ans += qsum(b , e , rson);
return ans;
}
int main()
{
int n , m , opt , x , y;
ll z;
scanf("%d%d" , &n , &m);
build(1 , n , 1);
while(m -- )
{
scanf("%d%d%d" , &opt , &x , &y) , x ++ , y ++ ;
if(opt == 1) scanf("%lld" , &z) , vadd(x , y , z , 1 , n , 1);
if(opt == 2) scanf("%lld" , &z) , vdiv(x , y , z , 1 , n , 1);
if(opt == 3) printf("%lld\n" , qmin(x , y , 1 , n , 1));
if(opt == 4) printf("%lld\n" , qsum(x , y , 1 , n , 1));
}
return 0;
}

【loj6029】「雅礼集训 2017 Day1」市场 线段树+均摊分析的更多相关文章

  1. loj#6029. 「雅礼集训 2017 Day1」市场(线段树)

    题意 链接 Sol 势能分析. 除法是不能打标记的,所以只能暴力递归.这里我们加一个剪枝:如果区间内最大最小值的改变量都相同的话,就变成区间减. 这样复杂度是\((n + mlogn) logV\)的 ...

  2. 「雅礼集训 2017 Day1」市场 (线段树除法,区间最小,区间查询)

    老师说,你们暴力求除法也整不了多少次就归一了,暴力就好了(应该只有log(n)次) 于是暴力啊暴力,结果我归天了. 好吧,在各种题解的摧残下,我终于出了一篇巨好看(chou lou)代码(很多结构体党 ...

  3. #6029. 「雅礼集训 2017 Day1」市场 [线段树]

    考虑到每次除法,然后加法,差距会变小,于是维护加法lazytag即可 #include <cstdio> #include <cmath> #define int long l ...

  4. LOJ #6029. 「雅礼集训 2017 Day1」市场 线段树维护区间除法

    题目描述 从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落. 有 \(n\) 个商贩,从\(0 \sim n - 1\) 编号,每个商 ...

  5. loj6029 「雅礼集训 2017 Day1」市场

    传送门:https://loj.ac/problem/6029 [题解] 考虑如果有一些近似连续的段 比如 2 2 2 3 3 3,考虑在除3意义下,变成0 0 0 1 1 1,相当于整体-2 又:区 ...

  6. LOJ6029「雅礼集训 2017 Day1」市场 (线段树)

    题面 从前有一个学校,在 O n e I n D a r k \rm OneInDark OneInDark 到任之前风气都是非常良好的,自从他来了之后,发布了一系列奇怪的要求,挟制了整个学校,导致风 ...

  7. [LOJ 6029]「雅礼集训 2017 Day1」市场

    [LOJ 6029] 「雅礼集训 2017 Day1」市场 题意 给定一个长度为 \(n\) 的数列(从 \(0\) 开始标号), 要求执行 \(q\) 次操作, 每次操作为如下四种操作之一: 1 l ...

  8. 【loj6029】「雅礼集训 2017 Day1」市场&&【uoj#228】基础数据结构练习题

    题解: 这两道题加上区间取min max应该算线段树几道比较不寻常的题目 其实也是挺好理解的 对于区间/d 显然在log次后就会等于0 而我们注意到如果区间中数都相等那么就可以一起除 也就是说每个区间 ...

  9. 【LOJ6029】「雅礼集训 2017 Day1」市场(线段树裸题)

    点此看题面 大致题意: 维护序列,支持区间加法,区间除法(向下取整),区间求\(min\)和区间求和. 线段树维护区间除法 区间加法.区间求\(min\)和区间求和都是线段树基本操作,因此略过不提. ...

随机推荐

  1. HDU1863(Kruskal+并查集水题)

    https://cn.vjudge.net/problem/HDU-1863 省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可). ...

  2. visual studio 插件

    一:VS10x Code Map(打开后台cs文件,插件列出了所有的字段.方法,方便当代码行多的时候查找,提升工作效率) 1:下载 https://marketplace.visualstudio.c ...

  3. Android开发四大组件之Service(具体解释篇)

    Android开发之四大组件--Service 一.Service 简单介绍 Service是android系统中的四大组件之中的一个(Activity.Service.BroadcastReceiv ...

  4. spark-windows(含eclipse配置)下本地开发环境搭建

    spark-windows(含eclipse配置)下本地开发环境搭建   >>>>>>注意:这里忽略JDK的安装,JDK要求是1.8及以上版本,请通过 java  ...

  5. 20155305《网络对抗》PC平台逆向破解(二)

    20155305<网络对抗>PC平台逆向破解(二) shellcode注入 1.shellcode shellcode是一段代码,溢出后,执行这段代码能开启系统shell. 2.构造方法 ...

  6. 20155338《网络对抗》Web安全基础实践

    20155338<网络对抗>Web安全基础实践 实验过程 WebGoat 在终端中输入 java -jar webgoat-container-7.0.1-war-exec.jar 开启W ...

  7. 汇编 LEA 指令

    知识点:  LEA指令  &与LEA  OD里修改汇编代码 一.LEA指令格式 有效地址传送指令 LEA 格式: LEA 操作数A, 操作数B 功能: 将操作数B的有效地址传送到指定的的 ...

  8. python 回溯法 子集树模板 系列 —— 1、8 皇后问题

    问题 8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 分析 为了简化问题,考虑到8个皇后不同行,则每一行放置一个皇后,每一行的 ...

  9. Scala学习(一)练习

    Scala基础学习&l练习 1. 在Scala REPL中键人3.,然后按Tab键.有哪些方法可以被应用 在Scala REPL中需要按3. 然后按Tab才会提示. 直接按3加Tab是没有提示 ...

  10. task1

    centos定时任务 清空特定目录文件 https://www.jb51.net/article/151066.htm 这次linux下不生成日志文件主要是因为日志框架冲突问题,我解决问题的思路错了 ...