Educational Codeforces Round 65 (Rated for Div. 2)E. Range Deleting(区间修改线段树)

想了个思路不敢交,发现 luogu 没有我的题解的思路,就麻利码出来交了,开水,逃

所在比赛传送门

假如完全非降,直接随便删就行

a n s = ∑ i = 1 n ( n − i + 1 ) ans=\sum_{i=1}^{n}(n-i+1) ans=i=1n(ni+1)

考虑只有一个下降的地方,答案并不改变

考虑有一个沟

n u m [ l ] < = n u m [ r ]   a n d   n u m [ l ] > max ⁡ l < i < r ( n u m [ i ] ) num[l]<=num[r]\ and\ num[l]>\max_{l<i<r}(num[i]) num[l]<=num[r] and num[l]>l<i<rmax(num[i])

我们发现要维护这序列的单调性,只需要关注两个区间就行,

[ min ⁡ l < i < r ( n u m [ i ] ) , max ⁡ l < i < r ( n u m [ i ] ) ] [\min_{l<i<r}(num[i]),\max_{l<i<r}(num[i])] [l<i<rmin(num[i]),l<i<rmax(num[i])]

[ l < i < r ( i f ( n u m [ i − 1 ] > n u m [ i ] ) ) ( min ⁡ 1 < j < i ( n u m [ j ] )   a n d   n u m [ j ] > n u m [ i ] ) , n u m [ l ] ] [_{l<i<r}(if(num[i-1]>num[i]))(\min_{1<j<i}(num[j])\ and \ num[j]>num[i]),num[l]] [l<i<r(if(num[i1]>num[i]))(1<j<imin(num[j]) and num[j]>num[i]),num[l]]

我们至少要删掉其中一个区间,因为只要包含这两个区间就行,我们可以在计算答案时枚举左端点,第二个区间我们命名为 [ l 1 , r 1 ] [l1,r1] [l1,r1],第一个是 [ l 2 , r 2 ] [l2,r2] [l2,r2],可以发现 l 1 < l 2 l1<l2 l1<l2的,细分为 3 3 3个区间, [ 1 , l 1 ] [1,l1] [1,l1] [ l 1 + 1 , l 2 ] [l1+1,l2] [l1+1,l2] [ l 2 + 1 , x ] [l2+1,x] [l2+1,x]左端点在第一个区间的,删除区间的右端点至少是r1,同理其它区间的左端点删除区间分别是 r 2 r2 r2、无解(因为一定不能删掉其中一个区间,我们用一个值 i n f inf inf表示它),区间修改当然是线段树辣,来个懒标暴力线段树

当考虑多个沟时,每个沟都得删除至少一个区间,注意到对应每一个左端点,它要删除的最小区间的右端点不能在经过一个沟之后变小,取个 max ⁡ \max max就行,我们用线段树的懒标也是取个 max ⁡ \max max

ac 代码

# include  <bits/stdc++.h>
using  namespace  std ;

using ll = long long ;
namespace predefine {
const long long inf = 0x3f3f3f3f3f3f3f3f ;
using ll = long long ;

# define  IOS  iostream::sync_with_stdio ( 0 ) ; cin.tie ( 0 ) ;

# define af( i , a , b ) for ( ll i = a ; i <= b ; ++ i )
# define df( i , a , b ) for ( ll i = a ; i >= b ; -- i )
}
using namespace predefine ;


template <typename T>
class SegtreeLazyRangeSet {
	vector<T> tree , lazy ;
	vector<T> *arr ;
	ll n , root , n4 , end ;

	void maintain ( ll cl , ll cr , ll p ){
		ll cm = cl + ( ( cr - cl ) >> 1 ) ;
		if ( cl != cr && lazy [ p ] ) {
			lazy [ p << 1 ] = max ( lazy [ p << 1 ] , lazy [ p ] ) ;
		    lazy [ p << 1 | 1 ] = max ( lazy [ p << 1 | 1 ] , lazy [ p ] ) ;
		}
	}
    void set_all ( ll l , ll r , ll p ) {
        if ( l == r ) {
            ( *arr ) [ l ] = max ( ( *arr ) [ l ] , (ll) lazy [ p ] ) ;\
            return ;
        }
        maintain ( l , r , p ) ;
        ll m = ( l + r ) >> 1 ;
        set_all ( l , m , p << 1 ) ; set_all ( m + 1 , r , p << 1 | 1 ) ;
    }
	void range_set ( ll l , ll r , T val , ll cl , ll cr , ll p ) {
		if ( l <= cl && r >= cr ) {
			lazy [ p ] = max ( lazy [ p ] , val ) ;
			return ;
		}
		ll m = cl + ( ( cr - cl ) >> 1 ) ;
		maintain ( cl , cr , p ) ;
		if ( l <= m ) range_set ( l , r , val , cl , m , p << 1 ) ;
		if ( r > m ) range_set ( l , r , val , m + 1 , cr , p << 1 | 1 ) ;
	}
public : 
	explicit SegtreeLazyRangeSet < T > ( vector < T >& v ) {
		n = v.size () ;
		n4 = n * 4 ;
		lazy = vector < T > ( n4 , 0 ) ;
		arr = &v ;
		end = n - 1 ;
		root = 1 ;
	}
	void show ( ll p , ll depth = 0 ) {
		if ( p > n4 || tree [ p ] == 0 ) return ;
		show ( p << 1 , depth + 1 ) ;
		for ( ll i = 0 ; i < depth ; ++ i ) putchar ( '\t' ) ;
		printf ( "%d:%d\n", tree [ p ] , lazy [ p ] ) ;
		show ( p << 1 | 1 , depth + 1 ) ;
	}

	T range_sum ( ll l , ll r ) { return range_sum ( l , r , 0 , end , root ) ; } 

	void range_set ( ll l , ll r , ll val ) { if ( l <= r ) range_set ( l , r , val , 0 , end , root ) ; } 

    void set_all () { set_all ( 0ll , end , root ) ; }
};

void solve ()
{
    ll n , x ; cin >> n >> x ;
    vector < ll > v ( n ) ;
    af ( i , 0 ,n - 1 ) cin >> v [ i ] , -- v [ i ] ;
    vector < ll > ans ( x ) ;
    iota ( ans.begin () , ans.end () , 0 ) ;
    SegtreeLazyRangeSet < ll > tree ( ans ) ;
    set < ll > s ; s.emplace ( v [ 0 ] ) ;
    af ( i , 1 , n - 1 ) {
    	s.emplace ( v [ i ] ) ;
        if ( v [ i ] < v [ i - 1 ] ) {
            ll l1 = v [ i ] , r1 = v [ i ] , l2 = * ( s.upper_bound ( v [ i ] ) ) , r2 = *( s.rbegin () ) ;
            ++ i ;
            while ( i <= n - 1 && v [ i ] < r2 ) {
                l1 = min ( l1 , v [ i ] ) ;
                r1 = max ( r1 , v [ i ] ) ;
                s.emplace ( v [ i ] ) ;
                if ( v [ i ] < v [ i - 1 ] ) {
                    auto tmp = s.upper_bound ( v [ i ] ) ; 
                    l2 = min ( *tmp , l2 ) ; 
                }
                ++ i ;
            }
            s.emplace ( v [ i ] ) ;
            tree.range_set ( 0      , l1    , r1     ) ;
            tree.range_set ( l1 + 1 , l2    , r2     ) ;
            tree.range_set ( l2 + 1 , x - 1 , inf    ) ;
        }
    }
    tree.set_all () ;
	ll ys = 0 ;
    af ( i , 0 , x - 1 ) {
        if ( ans [ i ] != inf ) {
            ys += ( x - 1 - ans [ i ] + 1 ) ;
        }
    }
    cout << ys << endl ;
}

int  main () {
	// IOS
	ll _ = 1 ;
//	cin >> _ ;
	while ( _ -- ) {
		solve () ;
	} 
	return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值