1. 程式人生 > >【hdu5306】Gorgeous Sequence 線段樹區間最值操作

【hdu5306】Gorgeous Sequence 線段樹區間最值操作

font 區間和 pro while turn lin int ace 退出

題目描述

給你一個序列,支持三種操作:

$0\ x\ y\ t$ :將 $[x,y]$ 內大於 $t$ 的數變為 $t$ ;
$1\ x\ y$ :求 $[x,y]$ 內所有數的最大值;
$2\ x\ y$ :求 $[x,y]$ 內所有數的和。

多組測試數據,$\sum n,\sum m\le 10^6$


題解

線段樹區間最值操作

對於線段樹上的一個節點,維護對應區間的:最大值 $mx$ 、最大值個數 $c$ 及嚴格次大值 $se$ 。那麽對於一次區間最小值操作:

如果 $t\ge mx$ ,則這個操作不會對區間產生影響,直接退出;
如果 $se<t<mx$ ,則這個操作只會對區間最大值產生影響,區間和減小 $c(mx-t)$ ,最大值變為 $t$ ,打標記退出;

否則,無法直接計算貢獻,遞歸子樹處理。

其中第二種情況的 “打標記” 實際上就是下傳新的最大值,因此可以不打標記,直接將最大值下傳。

這樣做的時間復雜度是 $O(n\log n)$ 的,證明參考 吉老師的Segment tree Beats!

#include <cstdio>
#include <algorithm>
#define N 1000010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
ll mx[N << 2] , c[N << 2] , se[N << 2] , sum[N << 2];
inline void vmin(ll v , int x)
{
	if(mx[x] > v) sum[x] -= c[x] * (mx[x] - v) , mx[x] = v;
}
inline void pushup(int x)
{
	int l = x << 1 , r = x << 1 | 1;
	sum[x] = sum[l] + sum[r];
	if(mx[l] > mx[r]) mx[x] = mx[l] , c[x] = c[l] , se[x] = max(se[l] , mx[r]);
	if(mx[l] < mx[r]) mx[x] = mx[r] , c[x] = c[r] , se[x] = max(mx[l] , se[r]);
	if(mx[l] == mx[r]) mx[x] = mx[l] , c[x] = c[l] + c[r] , se[x] = max(se[l] , se[r]);
}
inline void pushdown(int x)
{
	vmin(mx[x] , x << 1) , vmin(mx[x] , x << 1 | 1);
}
void build(int l , int r , int x)
{
	if(l == r)
	{
		scanf("%lld" , &mx[x]) , sum[x] = mx[x] , c[x] = 1 , se[x] = -1;
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	pushup(x);
}
void update(int b , int e , ll v , int l , int r , int x)
{
	if(mx[x] <= v) return;
	if(b <= l && r <= e && se[x] < v)
	{
		vmin(v , x);
		return;
	}
	pushdown(x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , v , lson);
	if(e > mid) update(b , e , v , rson);
	pushup(x);
}
ll qmax(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return mx[x];
	pushdown(x);
	int mid = (l + r) >> 1;
	ll ans = 0;
	if(b <= mid) ans = max(ans , qmax(b , e , lson));
	if(e > mid) ans = max(ans , qmax(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(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 T;
	scanf("%d" , &T);
	while(T -- )
	{
		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);
			if(opt == 0) scanf("%lld" , &z) , update(x , y , z , 1 , n , 1);
			if(opt == 1) printf("%lld\n" , qmax(x , y , 1 , n , 1));
			if(opt == 2) printf("%lld\n" , qsum(x , y , 1 , n , 1));
		}
	}
	return 0;
}

【hdu5306】Gorgeous Sequence 線段樹區間最值操作