zoukankan      html  css  js  c++  java
  • P4192 旅行规划(分块+凸包)

    P4192 旅行规划(分块+凸包)

    题目大意

    维护序列并支持两种操作

    • 区间加
    • 区间查询最大前缀和(指 (sum_{i=1}^xa_i)

    数据范围

    对于 $ 100 % $ 的数据,$ n,m le 100000。$

    解题思路

    分块经典题

    考虑直接对序列求前缀和,那么相当于区间加等差数列,这是很难合并的,不适合使用线段树,所以考虑分块,因为一个等差数列加上另一个等差还是等差数列,所以每块维护两个 tag (首项,公差),代表此块加的等差数列,同时我们可以 $ Theta (1)$ 求出 $ sum[i]$ 的值,sum[i] = add[x] + d[x] * (i - L[x] + 1) + a[i]

    考虑如何查询整块,发现以 i 为 x,sum[i] 为 y 的点 $ (i, sum[i])$ , 每个块做一下凸包,不管加怎样的等差数列,对答案可能有贡献的点一定在凸包上,且凸包进行操作后依然满足单峰状态,可以在凸包上三分查找

    代码如下

    const int N = 200500;
    int SIZ, BCNT;
    int st[505][505], tp[505];
    int L[N], R[N], bl[N];
    ll add[N], a[N], d[N];
    int m, n;
    
    void spread(int x) {
    	if (!d[x] && !add[x]) return;
    	ll tmp = add[x];
    	for (int i = L[x]; i <= R[x]; ++i) 
    		tmp += d[x], a[i] += tmp;
    	d[x] = add[x] = 0;
    }
    
    double K(int x, int y) {
    	return (double)(a[x] - a[y]) / (x - y);
    }
    
    void build(int x) {
    	spread(x); int *s = st[x], t = 0;
    	for (int i = L[x]; i <= R[x]; i++) {
    		while (t > 1 && K(s[t-1], s[t]) < K(s[t-1], i)) --t;
    		s[++t] = i;
    	}
    	tp[x] = t;
    }
    
    ll get(int x, int p) {
    	return (p - L[x] + 1) * d[x] + add[x] + a[p];
    }
    
    ll query(int x) {
    	int l = 1, r = tp[x], *s = st[x];
    	while (l <= r) {
    		int m1 = (l + r) >> 1, m2 = (m1 + r + 1) >> 1;
    		if (get(x, s[m1]) > get(x, s[m2])) r = m2 - 1;
    		else l = m1 + 1;
    	}
    	return get(x, s[r]);
    }
    
    int main() {
    	freopen ("hs.in","r",stdin);
    	freopen ("hs.out","w",stdout);
    	read(n); SIZ = sqrt(n), BCNT = n / SIZ;
    	if (BCNT * SIZ != n) BCNT++;
    	for (int i = 1;i <= n; i++) read(a[i]), a[i] += a[i-1];
    	for (int i = 1;i <= BCNT; build(i++)) { 
    		L[i] = R[i-1] + 1, R[i] = min(R[i-1] + SIZ, n);
    		for (int j = L[i];j <= R[i]; j++) bl[j] = i;
    	}
    	for (read(m); m; m--) {
    		ll op, x, y, k;
    		read(op), read(x), read(y);
    		if (op) {
    			ll ans = -1e15;
    			spread(bl[x]), spread(bl[y]);
    			if (bl[y] - bl[x] <= 1) {
    				for (int i = x;i <= y; i++) Mx(ans, get(bl[i], i));
    				printf ("%lld
    ", ans); continue;
    			}
    			
    			for (int i = x;i <= R[bl[x]]; i++) Mx(ans, get(bl[i], i));
    			for (int i = bl[x] + 1; i < bl[y]; i++) Mx(ans, query(i));
    			for (int i = L[bl[y]];i <= y; i++) Mx(ans, get(bl[i], i));
    			printf ("%lld
    ", ans);
    			
    		}
    		else {
    			read(k); ll tmp = 0;
    			spread(bl[x]), spread(bl[y]);
    			
    			if (bl[y] - bl[x] <= 1) {
    				for (int i = x;i <= y; i++) tmp += k, a[i] += tmp;
    				for (int i = y + 1;i <= R[bl[y]]; i++) a[i] += tmp;
    				for (int i = bl[y] + 1;i <= BCNT; i++) add[i] += tmp;
    				build(bl[x]), bl[x] != bl[y] && (build(bl[y]), 0);
    				continue;
    			}
    			
    			for (int i = x; i <= R[bl[x]] ; i++) tmp += k, a[i] += tmp;
    			for (int i = bl[x] + 1; i < bl[y] ; i++) d[i] += k, add[i] += tmp, tmp += SIZ * k;
    			for (int i = L[bl[y]]; i <= y ; i++) tmp += k, a[i] += tmp;
    			for (int i = y + 1;i <= R[bl[y]]; i++) a[i] += tmp;
    			for (int i = bl[y] + 1;i <= BCNT; i++) add[i] += tmp;
    			build(bl[x]), build(bl[y]);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    浏览器的垃圾回收机制
    vue-router传参数的方式
    Vue插槽
    自定义事件
    vue计算属性和监听器
    vue绑定样式
    循环中使用同步请求
    小白之路 | 从小学一年级期末考试看servlet+jsp技术
    Java实现简单计算器的探索性做法
    分布式数据库NoSQL简介
  • 原文地址:https://www.cnblogs.com/Hs-black/p/12755104.html
Copyright © 2011-2022 走看看