zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    请你维护一个序列,支持两种操作:
    (1)某个区间 [x, y] 内的数同时加上一个增量 k。
    (2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和。

    input
    第一行给出一个整数 n。n <= 100000。接下来一行 n 个整数表示序列的初始值。
    第三行给出一个整数 m,m <= 100000。接下来 m 行每行一个操作。
    (1) 0 x y k:表示给区间 [x, y] 同时加上 k。
    (2) 1 x y:询问区间 [x, y]。
    output
    对于每个询问,输出一个整数表示最大前缀和。

    sample input
    5
    1 8 -8 3 -7
    3
    1 1 5
    0 1 3 6
    1 2 4
    sample output
    9
    22

    @solution@

    我们考虑直接维护前缀和序列,则操作(2)就是在查询区间最大值。

    而对于操作(1),我们相当于两部分操作:
    对于 x <= i <= y,给 i 位置加上 (i-x+1)*k;对于 y < i,给 i 位置加上 (y-x+1)*k。
    前一个可以拆成 (-x+1)*k + i*k,是常数 + 系数*位置的形式;后面那个也可以看成这种形式,只是位置前面的系数为 0。

    看起来还是不好维护,但是我们可以注意到这样一件事情:对于某一个位置 i,它的值总是形如 k*i + b 的形式。
    直线解析式。所以我们考虑用几何方法来维护这种东西。几何方法当然首先想到凸包。

    每次操作相当于给区间内所有点的斜率与截距同时加上增量,手算一下会发现这个区间相邻两点之间的斜率也会同时增加 k,这样也就是说这个区间的凸包形状不会变化。

    线段树不太好搞(况且这道题时限 50s ),我们考虑使用分块算法来维护凸包。
    修改时,散块暴力修改并重构凸包,整块打标记,记录这个区间整体的斜率与截距变化量。
    查询时,散块暴力求答案,整块凸包上二分。

    时间复杂度 (O(nsqrt{n}log n))

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 100000;
    const int BLOCK = 320;
    const ll INF = (1ll<<62);
    ll sp[BLOCK + 5], b1[MAXN + 5], b2[BLOCK + 5];
    int le[BLOCK + 5], ri[BLOCK + 5], num[MAXN + 5];
    int stk[MAXN + 5], tp[BLOCK + 5], n, m, bcnt = 0;
    ll get_ans(int x) {
    	return sp[num[x]]*x + b1[x] + b2[num[x]];
    }
    ll query(int x) {
    	int l = le[x], r = tp[x];
    	while( l < r ) {
    		int mid = (l + r) >> 1;
    		if( get_ans(stk[mid]) >= get_ans(stk[mid+1]) ) r = mid;
    		else l = mid + 1;
    	}
    	return get_ans(stk[r]);
    }
    void push_tag(int x) {
    	for(int i=le[x];i<=ri[x];i++)
    		b1[i] = get_ans(i);
    	sp[x] = b2[x] = 0;
    }
    void build(int x) {
    	tp[x] = le[x] - 1;
    	for(int i=le[x];i<=ri[x];i++) {
    		while( tp[x] > le[x] && (get_ans(i) - get_ans(stk[tp[x]]))*(stk[tp[x]] - stk[tp[x] - 1]) >= (get_ans(stk[tp[x]])-get_ans(stk[tp[x] - 1]))*(i - stk[tp[x]]) )
    			tp[x]--;
    		stk[++tp[x]] = i;
    	}
    }
    void init() {
    	for(int i=0;i<n;i++) {
    		if( i % BLOCK == 0 ) {
    			num[i] = (++bcnt);
    			le[num[i]] = ri[num[i]] = i;
    			sp[num[i]] = b2[num[i]] = 0;
    		}
    		else ri[num[i] = bcnt]++;
    	}
    }
    int main() {
    	scanf("%d", &n); init();
    	for(int i=0;i<n;i++)
    		scanf("%lld", &b1[i]), b1[i] += b1[i-1];
    	for(int i=1;i<=bcnt;i++)
    		build(i);
    	scanf("%d", &m);
    	for(int i=1;i<=m;i++) {
    		int op, x, y; ll k;
    		scanf("%d%d%d", &op, &x, &y);
    		x--, y--;
    		if( op == 0 ) {
    			scanf("%lld", &k);
    			if( num[x] != num[y] ) {
    				push_tag(num[x]), push_tag(num[y]);
    				for(int i=x;i<=ri[num[x]];i++)
    					b1[i] += k*(i-x+1);
    				for(int i=le[num[y]];i<=y;i++)
    					b1[i] += k*(i-x+1);
    				for(int i=y+1;i<=ri[num[y]];i++)
    					b1[i] += k*(y-x+1);
    				build(num[x]), build(num[y]);
    				for(int i=num[x]+1;i<=num[y]-1;i++)
    					sp[i] += k, b2[i] += k*(-x+1);
    			}
    			else {
    				push_tag(num[x]);
    				for(int i=x;i<=y;i++)
    					b1[i] += k*(i-x+1);
    				for(int i=y+1;i<=ri[num[y]];i++)
    					b1[i] += k*(y-x+1);
    				build(num[x]);
    			}
    			for(int i=num[y]+1;i<=bcnt;i++)
    				b2[i] += k*(y-x+1);
    		}
    		else {
    			ll ans = -INF;
    			if( num[x] != num[y] ) {
    				for(int i=x;i<=ri[num[x]];i++)
    					ans = max(ans, get_ans(i));
    				for(int i=le[num[y]];i<=y;i++)
    					ans = max(ans, get_ans(i));
    				for(int i=num[x]+1;i<=num[y]-1;i++)
    					ans = max(ans, query(i));
    			}
    			else {
    				for(int i=x;i<=y;i++)
    					ans = max(ans, get_ans(i));
    			}
    			printf("%lld
    ", ans);
    		}
    	}
    }
    

    @details@

    突然发现这是我第一次写分块?原来我以前从来没写过这种东西?
    把分块的左端点右端点以及每个点属于哪个块先预处理出来感觉比较好写。
    并且把块的大小设置为常数也是一个不错的懒人做法(虽然想想都知道这样肯定常数大)。

  • 相关阅读:
    mysql5.7一颗B+树可以存放多少行数据?为什么使用B+树而不是B树?
    mysql5.7的锁:乐观锁/共享锁、互斥/排他锁、意向锁、记录锁、行锁/表锁、间隙锁、临界锁、插入意向锁、自增锁、空间索引预测锁、隐式锁
    mysql5.7事务的原理和MVCC,redo log与bin log的区别
    mysql5.7 Buffer Pool特性介绍。innodb三大特性:双写缓冲区、Buffer Pool、AHI(自适应HASH索引)
    mysql5.7 innodb数据字典
    mysql5.7系统表空间和独立表空间,断,组,区,页的概念,innodb双写缓冲区
    mysql5.7行数据存储格式
    mysql5.7全局考虑性能化,SQL优化的最后一步:profile性能分析
    mysql5.7innodb引擎底层分析:子查询种类回顾
    mysql5.7强制指定驱动表与被驱动表straight_join
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10262494.html
Copyright © 2011-2022 走看看