zoukankan      html  css  js  c++  java
  • Codeforces 1136E Nastya Hasn't Written a Legend (线段树教做人系列)

    题意:有一个数组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
    ", query(1, 1, n, x, y));
    		}
    	}
    }
    

      

  • 相关阅读:
    JS选项卡
    JS字符串的方法
    JS数组的方法
    JS最原始封装素数,创建表格,改变样式,颜色等
    CS href,src,url混淆问题
    CS菜单常用布局及三角形
    CS两种添加图标布局
    CS两种图片文字常用布局
    CSS div的三种结构水平垂直包含margin的计算
    224. Basic Calculator
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/10527569.html
Copyright © 2011-2022 走看看