【loj6029】「雅礼集训 2017 Day1」市场 线段树+均摊分析

题目描述

给出一个长度为 $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;
}

原文地址:https://www.cnblogs.com/GXZlegend/p/8715279.html

时间: 2024-08-24 23:31:15

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

loj6029 「雅礼集训 2017 Day1」市场

传送门:https://loj.ac/problem/6029 [题解] 考虑如果有一些近似连续的段 比如 2 2 2 3 3 3,考虑在除3意义下,变成0 0 0 1 1 1,相当于整体-2 又:区间增加很容易造成这种段,所以我们猜测可以暴力维护 用一棵线段树即可.(好像真的能暴力维护啊 我不知道怎么证明复杂度) # include <stdio.h> # include <string.h> # include <iostream> # include <al

loj6045 「雅礼集训 2017 Day8」价

传送门:https://loj.ac/problem/6045 [题解] 由于存在完美匹配,所以选择k个药就要选择>=k个药材,我们要求的是选择k个药正好选择k个药材. 那么定义选一种减肥药的代价为-pi+INF,选一种药材的代价为INF,这样最小割肯定是恰好选k个 那么 最后答案就是最小割 - Σ(-pi+INF) [由于减肥药是负的所以要反过来...] 中间连的边要设成比inf大的.. # include <stdio.h> # include <string.h> #

「雅礼集训 2017 Day5」矩阵

填坑填坑.. 感谢wwt耐心讲解啊.. 如果要看这篇题解建议从上往下读不要跳哦.. 30pts 把$A$和$C$看成$n$个$n$维向量,那$A_i$是否加入到$C_j$中就可以用$B_{i,j}$表示了 枚举矩阵$A$,求出它的秩$r$,如果$C$在$A$的线性空间内则$C$可以被$A$表示出来 那么$B$矩阵的方案数就是$(2^{n-r})^n$ 这时候我们可以发现,由于枚举$A$覆盖了所有情况,秩相同的$C$的答案都是一样的 然后就可以打表算答案了.. 60pts 如果不想看可以跳过这段

LOJ #6035.「雅礼集训 2017 Day4」洗衣服 贪心

这道题的贪心好迷啊~我们对于两个过程进行单独贪心,然后再翻转一个,把这两个拼起来.先说一下单独贪心,单独贪心的话就是用一个堆,每次取出最小的,并且把这个最小的加上他单次的,再放进去.这样,我们得到的结果,是对于某些洗衣机,不停地洗,然后把这些洗衣机的时间,混在一起,排个序,由于对于每个洗衣机,如果被用到,那么他就会被不停地用,如果我们稍作改动,就一定会是用小的换来大的,所以这样最优.我们把两个拼起来为什么是对的呢.对于两个单独的答案,最优的无疑是,翻转之后拼起来,因为如果不是这样,也就是说进行了

#6030. 【雅礼集训 2017 Day1】矩阵

#6030. 「雅礼集训 2017 Day1」矩阵 题目描述 有一个 n×n  的矩阵,每个位置 (i,j) 如果是 . 表示为白色,如果是 # 表示为黑色. 初始时,每个位置可以是黑色或白色的,(i,j)  位置的值会作为 ai,j 给你. 现在有一种操作,选择两个整数 i,j∈[1,n],记 (i,1),(i,2),…,(i,n) (i, 1), (i, 2)的颜色为 C1,C2,…Cn ??,将 (1,j),(2,j),…,(n,j)  的颜色赋为 C1,C2,…,Cn ??. 你的任务是

「6月雅礼集训 2017 Day10」quote

[题目大意] 一个合法的引号序列是空串:如果引号序列合法,那么在两边加上同一个引号也合法:或是把两个合法的引号序列拼起来也是合法的. 求长度为$n$,字符集大小为$k$的合法引号序列的个数.多组数据. $1 \leq T \leq 10^5, 1 \leq n \leq 10^7, 1\leq K \leq 10^9$ [题解] 显然引号序列可以看做括号序列,于是我们有了一个$O(n^2)$的dp了. 设$f_{i,j}$表示到第$i$个位置,前面有$j$个左引号没有匹配,的方案数 每次,要么有

「6月雅礼集训 2017 Day7」电报

[题目大意] 有n个岛屿,第i个岛屿有有向发射站到第$p_i$个岛屿,改变到任意其他岛屿需要花费$c_i$的代价,求使得所有岛屿直接或间接联通的最小代价. $1 \leq n \leq 10^5, 1 \leq p_i,c_i \leq 10^9$ [题解] 显然最后是个大环,特判原来就是大环的情况. 考虑每个连通块最多保留多少. 树的答案可以直接dp做出来. 环的答案,根据树的答案dp出来. h[x][0/1]表示当前做到环上第i个点,环是否被切断了,的最大保留价值. 因为环必须被切断一次.所

「6月雅礼集训 2017 Day8」gcd

[题目大意] 定义times(a, b)表示用辗转相除计算a和b的最大公约数所需步骤. 那么有: 1. times(a, b) = times(b, a) 2. times(a, 0) = 0 3. times(a, b) = times(b, a mod b) + 1 对于$1 \leq x \leq A, 1 \leq y \leq B$,求times(A, B)的最大值,以及有多少对数取到了最大值. 多组数据. $T \leq 3 \times 10^5, 1 \leq A,B \leq

「6月雅礼集训 2017 Day7」三明治

[题目大意] $1 \leq n,m \leq 400$ N字形表示:上图第1行第1个那种:Z字形表示上图第1行第2个那种. [题解] 很容易得到结论: 考虑如果紫色比绿色先消去,那么黄色一定会比对应的白色先消去(这样才能消去白色). 然后我们可以知道,设取走$(x, y)$靠左的正方形,要$L(x,y)$步,那么$L(x,y) \geq L(x,y-1)$,同理也有$L(x, y) \geq L(x, y+1)$. 然后我们对于这个就可以进行一行一行的dfs了,dfs的时候可能会搜到原来已经搜