ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

D. Progressions Covering_线段树维护差分

2022-04-14 20:01:17  阅读:199  来源: 互联网

标签:数列 Covering ai ll 差分 区间 Progressions 线段


线段树维护差分

D. Progressions Covering

题目大意:

数列a原来全是0,可以无限次进行一种操作,每次操作可以选择一段长度为k的区间,对该区间的数字分别对应加上1,2,3,...,k。再给出数列b,问最少操作几次可以使得a数列的每一个数字不小于b数列中的对应数字。

思路和代码:

可以操作题目给出的数组,将其中每一个数减到小于等于0。(当然也可以操作0数组,然后把每个数加到对应大于等于,都可以)

实话说,看到等差数列就会去想维护一个差分线段树。

每个数要减到小于等于0。对于第i个数ai,要减到小于等于0,最赚的就是把它放在区间的尽可能的右端。所以我们从后往前减数即可。

有了以上思路暴力去做复杂度就是O(nk)。遍历每个数的O(n)是不能优化的,那么考虑去优化这个O(k),即区间等差数列加减法。将等差数列加减法放到差分数列上去做,即可转化为区间加减法前缀和(区间和)问题。

当然本题要注意当i走到了区间长k的前面,我们就不能把当前i位置放到区间最右边,不然回超出区间。所以前面说的是尽可能的右端。

ll n , m , k ; 

ll a[N] ;

struct Node{
	int l , r ;
	ll sum , lzy ;
}tr[N << 2];

void pushup(Node &f , Node &l , Node &r){
	f.sum = l.sum + r.sum ;
}

void update(Node &u , ll det){
	u.sum += 1LL * (u.r - u.l + 1) * det ;
	u.lzy += 1LL * det ;
}

void pushdown(Node &f , Node &l , Node &r){
	update(l , f.lzy) ;
	update(r , f.lzy) ;
	f.lzy = 0 ;
}

ll query(int now , int l , int r){ // 差分数组单点查询
//	cout << tr[now].l << "_" << tr[now].r << "\n" ;
	if(l <= tr[now].l && tr[now].r <= r) return tr[now].sum ;
	
	pushdown(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
	
	int mid = tr[now].l + tr[now].r >> 1 ;
	ll res = 0 , lft = 0 , rit = 0 ;
	if(l <= mid) lft = query(now << 1 , l , r) ;
	if(mid < r ) rit = query(now << 1 | 1 , l , r) ;
	res = lft + rit ;
	return res ;
}

void modify(int now , int l , int r , ll det){
	if(l <= tr[now].l && tr[now].r <= r){
		update(tr[now] , det) ;
		return ;
	}
	pushdown(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
	int mid = tr[now].l + tr[now].r >> 1 ;
	if(l <= mid) modify(now << 1 , l , r , det) ;
	if(mid < r ) modify(now << 1 | 1 , l , r , det) ;
	pushup(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
}

void build(int now , int l , int r){ //cout << l << " " << r << "\n" ;
	if(l == r){
		tr[now] = {l , r , a[l] - a[l - 1] , 0} ;
		return ;
	}
	tr[now] = {l , r , 0 , 0} ;
	int mid = l + r >> 1 ;
	build(now << 1 , l , mid) ;
	build(now << 1 | 1 , mid + 1 , r) ;
	pushup(tr[now] , tr[now << 1] , tr[now << 1 | 1]) ;
}

void solve(){
	cin >> n >> k ;
	
	rep(i , 1 , n) cin >> a[i] ;
	build(1 , 1 , n) ;
	
	ll ans = 0 ;
	
	for(int i = n ; i >= 1 ; i -- ){
		ll ai = query(1 , 1 , i) ;
		if(ai > 0LL){
			ll L = min(1LL * i , 1LL * k) ;
			ll d = (ai + L - 1) / L ;
			ans += d ;
			modify(1 , i - L + 1 , i , -1LL * d) ;
			if(i + 1 <= n)modify(1 , i + 1 , i + 1 , -1LL * L * d) ;
		}
	}
	cout << ans << "\n" ;
}//code_by_tyrii 
小结:

线段树格外注意边界

标签:数列,Covering,ai,ll,差分,区间,Progressions,线段
来源: https://www.cnblogs.com/tyriis/p/16146259.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有