1. 程式人生 > >【bzoj4355】Play with sequence 線段樹區間最值操作

【bzoj4355】Play with sequence 線段樹區間最值操作

max ans 時間 query sam define sca tps fine

題目描述

維護一個長度為N的序列a,現在有三種操作: 1)給出參數U,V,C,將a[U],a[U+1],...,a[V-1],a[V]都賦值為C。 2)給出參數U,V,C,對於區間[U,V]裏的每個數i,將a[i]賦值為max(a[i]+C,0)。 3)給出參數U,V,輸出a[U],a[U+1],...,a[V-1],a[V]裏值為0的數字個數。

輸入

第一行包含兩個正整數N,M(1<=N,M<=300000),分別表示序列長度和操作個數。 第二行包含N個整數,其中第i個數表示a[i](0<=a[i]<=10^9),描述序列的初始狀態。 接下來M行描述M個操作,保證1<=U<=V<=N,對於操作1,0<=C<=10^9,對於操作2,|C|<=10^9。

輸出

輸出若幹行,每行一個整數,依次回答每個操作3的問題。

樣例輸入

5 3
6 4 6 6 4
2 1 5 -5
1 3 4 4
3 1 5

樣例輸出

2


題解

線段樹區間最值操作

考慮到操作1的 $C\le 0$ ,因此 $0$ 只可能出現在最小值。所以要統計 $0$ 的個數,只需要統計:最小值是不是0、最小值個數即可。

對於操作1直接區間賦值,操作2我們拆成兩個操作:區間+C直接加,區間最大值操作參考 吉老師的Segment tree Beats! ,維護最小值、嚴格次小值即可。

註意標記下傳順序:區間賦值>區間加>區間最大操作。

同樣,區間最大操作可以不維護標記,直接下傳最小值。

時間復雜度 $O(n\log^2 n)$ (吉老師表示PPT裏的證明是萎的...復雜度證明參考集訓隊論文)

#include <cstdio>
#include <algorithm>
#define N 1200010
#define inf 1ll << 62
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
typedef long long ll;
ll mn[N] , se[N] , cov[N] , add[N];
int mc[N];
inline void pushup(int x)
{
	int ls = x << 1 , rs = x << 1 | 1;
	if(mn[ls] < mn[rs]) mn[x] = mn[ls] , mc[x] = mc[ls] , se[x] = min(se[ls] , mn[rs]);
	if(mn[ls] > mn[rs]) mn[x] = mn[rs] , mc[x] = mc[rs] , se[x] = min(mn[ls] , se[rs]);
	if(mn[ls] == mn[rs]) mn[x] = mn[ls] , mc[x] = mc[ls] + mc[rs] , se[x] = min(se[ls] , se[rs]);
}
inline void pushdown(int l , int r , int x)
{
	int ls = x << 1 , rs = x << 1 | 1;
	if(~cov[x])
	{
		int mid = (l + r) >> 1;
		mn[ls] = cov[x] , mc[ls] = mid - l + 1 , se[ls] = inf , cov[ls] = cov[x] , add[ls] = 0;
		mn[rs] = cov[x] , mc[rs] = r - mid , se[rs] = inf , cov[rs] = cov[x] , add[rs] = 0;
		cov[x] = -1;
	}
	if(add[x])
	{
		mn[ls] += add[x] , se[ls] += add[x] , add[ls] += add[x];
		mn[rs] += add[x] , se[rs] += add[x] , add[rs] += add[x];
		add[x] = 0;
	}
	if(mn[ls] < mn[x]) mn[ls] = mn[x];
	if(mn[rs] < mn[x]) mn[rs] = mn[x];
}
void build(int l , int r , int x)
{
	cov[x] = -1;
	if(l == r)
	{
		scanf("%lld" , &mn[x]) , mc[x] = 1 , se[x] = inf;
		return;
	}
	int mid = (l + r) >> 1;
	build(lson) , build(rson);
	pushup(x);
}
void cover(int b , int e , ll c , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		mn[x] = c , mc[x] = r - l + 1 , se[x] = inf , cov[x] = c , add[x] = 0;
		return;
	}
	pushdown(l , r , x);
	int mid = (l + r) >> 1;
	if(b <= mid) cover(b , e , c , lson);
	if(e > mid) cover(b , e , c , rson);
	pushup(x);
}
void update(int b , int e , ll a , int l , int r , int x)
{
	if(b <= l && r <= e)
	{
		mn[x] += a , se[x] += a , add[x] += a;
		return;
	}
	pushdown(l , r , x);
	int mid = (l + r) >> 1;
	if(b <= mid) update(b , e , a , lson);
	if(e > mid) update(b , e , a , rson);
	pushup(x);
}
void vmax(int b , int e , int l , int r , int x)
{
	if(mn[x] >= 0) return;
	if(b <= l && r <= e && se[x] > 0)
	{
		mn[x] = 0;
		return;
	}
	pushdown(l , r , x);
	int mid = (l + r) >> 1;
	if(b <= mid) vmax(b , e , lson);
	if(e > mid) vmax(b , e , rson);
	pushup(x);
}
int query(int b , int e , int l , int r , int x)
{
	if(b <= l && r <= e) return mn[x] ? 0 : mc[x];
	pushdown(l , r , x);
	int mid = (l + r) >> 1 , ans = 0;
	if(b <= mid) ans += query(b , e , lson);
	if(e > mid) ans += query(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);
		if(opt == 1) scanf("%lld" , &z) , cover(x , y , z , 1 , n , 1);
		if(opt == 2) scanf("%lld" , &z) , update(x , y , z , 1 , n , 1) , vmax(x , y , 1 , n , 1);
		if(opt == 3) printf("%d\n" , query(x , y , 1 , n , 1));
	}
	return 0;
}

【bzoj4355】Play with sequence 線段樹區間最值操作