【loj6029】「雅禮集訓 2017 Day1」市場 線段樹+均攤分析

題目描述html

給出一個長度爲 $n$ 的序列,支持 $m$ 次操做,操做有四種:區間加、區間下取整除、區間求最小值、區間求和。數據結構

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


題解spa

線段樹+均攤分析htm

【uoj#228】基礎數據結構練習題 相似的均攤分析題。blog

對於原來的兩個數 $a$ 和 $b$ ( $a>b$ ) ,原來的差是 $a-b$ ,都除以 $d$ 後的差是 $\frac{a-b}d$ ,至關於差也除了 $d$ 。get

而當區間差爲 $0$ 或 $a=kd,b=kd-1$ 的 $1$ 時,區間下取整除就變成了區間減。it

所以當一個區間下取整除了 $\log(Max-Min)$ 次後就不須要暴力下取整除,直接區間減便可。io

定義線段樹節點勢能爲 $\log(Max-Min)$ ,那麼每次對 $[l,r]$ 下取整除就是將全部 $l\le x,y\le r$ ,且勢能不爲 $0$ 的節點 $[x,y]$ 的勢能減 $1$ ,代價爲勢能減小總量。class

分析區間加操做:只會修改到通過的節點的勢能,影響 $\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;
}
相關文章
相關標籤/搜索