1. 程式人生 > >Codeforces 1136E Nastya Hasn't Written a Legend (線段樹教做人系列)

Codeforces 1136E Nastya Hasn't Written a Legend (線段樹教做人系列)

its 前綴和 tmp 假設 val fin 線段 麻煩 區間和

題意:有一個數組a和一個數組k,數組a一直保持一個性質:a[i + 1] >= a[i] + k[i]。有兩種操作:1,給某個元素加上x,但是加上之後要保持數組a的性質。比如a[i]加上x之後,a[i + 1]<a[i] + k[i],那麽a[i + 1]就變成a[i] + k[i],否則不變。同理,若a[i + 2]小於了現在的a[i + 1] + k[i + 1],那麽a[i + 2]也變成a[i + 1] + k[i + 1],一直保持這個性質。第二章操作,詢問數組a的區間[l, r]的區間和。

思路:容易發現,假設更改了x位置之後,恰好到位置y不需要更改元素,那麽及其之後的位置肯定就不用更改了。所以,在查找這個位置的時候,我們可以二分查找。找到之後,對區間[x, y]進行操作。我們可以發現,區間[x, y]的數有規律。假設x位置的數是a[x],那麽a[x + 1]是a[x] + k[x], a[x + 2]是a[x] + k[x + 1] + k[x + 2],以此類推。這個區間的和可以分為2部分:[y - x + 1]個a[x],和關於k的部分。可以發現,k[x]出現了(r - l + 1)次,k[x + 1]出現了(r - l)次,以此類推。那麽怎麽O(1)獲得這個東西呢?我們先預處理k數組的前綴和(假設前綴和數組是b),那麽我們再求b的前綴和(假設是數組c),那麽c[i]就是出現了i次k[1],i - 1次k[2],以此類推。那麽區間[x, y]中關於k的和就可以得出了:c[y] - c[x - 1] - [y - x + 1] * b[x]。前面c[y] - c[x - 1]可以O(1)算出,而後面的部分比較麻煩。怎麽維護後面[y - x + 1] * b[x]這個部分呢?我們發現a[x]正好出現[y - x + 1]次,這樣我們可以把a[x] - b[x]用一個懶標記維護,下放標記的時候區間的和為c[y] - c[x - 1] + (r - l + 1) * val (val是a[x] - b[x])。

代碼:

#include <bits/stdc++.h>
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define LL long long
using namespace std;
const int maxn = 100010;
const LL INF = 1e18;
LL a[maxn], b[maxn], c[maxn];
int n, m, now;
struct SegementTree{
	LL sum, tot;
	int l, r;
};
SegementTree tr[maxn * 4];

LL cal(int l, int r) {
	return (c[r] - c[l]) - (r - l) * (b[l]);
}
void pushup(int x) {
	tr[x].sum = tr[ls(x)].sum + tr[rs(x)].sum;
}

void maintain(int x,LL tot) {
	if(tot == -INF) return;
	int l = tr[x].l, r = tr[x].r;
	tr[x].sum = (r - l + 1) * tot + (c[r] - c[l - 1]);
	tr[x].tot = tot;
}

void pushdown(int x) {
	maintain(ls(x), tr[x].tot);
	maintain(rs(x), tr[x].tot);
	tr[x].tot = -INF;
}

void build(int x, int l, int r) {
	tr[x].l = l, tr[x].r = r;
	tr[x].tot = -INF;
	if(l == r) {
		tr[x].sum = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(ls(x), l, mid);
	build(rs(x), mid + 1, r);
	pushup(x);
}

LL query(int x, int l, int r, int ql, int qr) {
	if(l >= ql && r <= qr) {
		return tr[x].sum;
	}
	int mid = (l + r) >> 1;
	LL ans = 0;
	pushdown(x);
	if(ql <= mid) ans += query(ls(x), l, mid, ql, qr);
	if(qr > mid) ans += query(rs(x), mid + 1, r, ql, qr);
	return ans;
}

void update(int x, int l, int r, int ql, int qr, LL val) {
	if(l >= ql && r <= qr) {
		maintain(x, val);
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(x);
	if(mid >= ql) update(ls(x), l, mid, ql, qr, val);
	if(mid < qr) update(rs(x), mid + 1, r, ql, qr, val);
	pushup(x);
}

int main() {
	int x, y;
	char s[5];
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &a[i]);
	}
	for (int i = 2; i <= n; i++) {
		scanf("%lld", &b[i]);
		b[i] += b[i - 1];
	}
	for (int i = 2; i <= n; i++)
		c[i] = c[i - 1] + b[i];
	build(1, 1, n);
	scanf("%d", &m);
	while(m--) {
		scanf("%s%d%d", s + 1, &x, &y);
		if(s[1] == ‘+‘) {
			int l = x, r = n;
			LL tmp = query(1, 1, n, x, x);
			while(l < r) {
				int mid = (l + r + 1) >> 1;
				LL tmp1 = query(1 , 1, n, mid, mid);
				if(tmp + y + b[mid] - b[x] > tmp1)
					l = mid;
				else r = mid - 1;
			}
			now = x;
			update(1, 1, n, x, l, tmp + y - b[x]);
		} else {
			printf("%lld\n", query(1, 1, n, x, y));
		}
	}
}

  

Codeforces 1136E Nastya Hasn't Written a Legend (線段樹教做人系列)