• bzoj5089 最大连续子段和 分块+复杂度分析+凸包


    题目传送门

    https://lydsy.com/JudgeOnline/problem.php?id=5089

    题解

    本来打算迟一点再写这个题解的,还有一个小问题没有弄清楚。

    不过先写一下存个档吧。


    如果只是单点修改,我们的常见做法是维护 (ls, rs, s) 表示前缀和最大值,后缀和最大值,区间最大子段和,然后进行区间合并,线段树维护。

    但是这个在这里显然是行不通的,因为我们不是单点修改,我们需要考虑一个加标记对于整个连续段的影响。

    对于一个长度为 (k) 的子段,初始的时候它的和为 (b),如果增加量为 (x),那么现在的值应该是 (kx+b)

    很容易发现,这个现在的值与 (x) 是线性关系,然后我们在一个段中,显然是要把所有的这样的直线在某一个 (x) 的位置取 (max)

    所以可以用凸包来维护一下这个东西。每一次询问的时候,因为加标记的值一定是递增的,所以只需要在凸包上移动指针就可以了。

    可以看出,如果一段的长度为 (b),那么构造这样的凸包的复杂度为 (O(b^2))


    但是如果使用线段树的话,被影响到的不仅用整段,还有这些整段的所有祖先,而加标记在祖先上却不是满的,所以要重构的段有 (log) 段,每一次重构的复杂度为 (O(b^2)),而在线段树上,(b) 最长可以达到 (n)。所以使用线段树不是一个明智的选择。

    那么我们考虑使用每一段的长度有保证的分块。


    设每一块的长度为 (b)

    对于一开始的构造操作,需要枚举每一块来构造,时间复杂度为 (O(frac nb cdot b^2) = O(nb))

    对于修改操作,如果是整块可以直接打标记,如果是零散的块,就直接暴力重构,只需要重构两块,每一次重构 (O(b^2)),所以复杂度为 (O(frac nb + b^2))

    对于查询操作,如果是整块直接在凸包上移动指针,显然从始至终,指针移动的总幅度不超过 (b),所以可以看成均摊 (O(1)),对于散块,也是直接暴力做最大字段和,复杂度 (O(b))。因此这里的复杂度为 (O(frac nb + b))


    我们取上面复杂度最高的 (O(frac nb +b^2)) 来分析。我们需要让这个东西最小,根据基本不等式,当 (frac nb +b^2) 的时候可以满足这个条件。

    所以 (b) 应该取 (sqrt[3]n),即 (n^{frac 13})

    那么初始化的复杂度为 (O(n^{frac 43})),单次询问的复杂度为 (O(n^{frac 23})),单次修改的复杂度也是 (O(n^{frac 23}))


    但是我还有一个问题没有解决:

    指针在凸包上的总移动次数不超过 (O(b)) 是因为加的一直是正数,也就是加标记越来越大。

    但是被暴力重构的块的凸包形态会改变啊。这个会不会破坏上面的复杂度分析呢。


    不考虑上面的这个问题,这个题目的时间复杂度为 (O(n^{frac 43}+mn^{frac 23})),因为 (n, mleq 50000),勉强可以通过。

    但是我怎么比暴力还慢啊。


    #include<bits/stdc++.h>
    
    #define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
    #define dbg(...) fprintf(stderr, __VA_ARGS__)
    #define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
    #define fi first
    #define se second
    #define pb push_back
    
    template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
    template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
    
    typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
    
    template<typename I> inline void read(I &x) {
    	int f = 0, c;
    	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    	x = c & 15;
    	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    	f ? x = -x : 0;
    }
    
    const int N = 50000 + 7;
    const int B = 40 + 7;
    
    #define bl(x) (((x) - 1) / blo + 1)
    #define st(x) (((x) - 1) * blo + 1)
    #define ed(x) std::min((x) * blo, n)
    
    int n, m, blo;
    int a[N], add[B * B];
    ll sum[B * B];
    
    struct Line {
    	ll k, b;
    	inline Line() {}
    	inline Line(const ll &k, const ll &b) : k(k), b(b) {}
    	inline ll get_y(const int &x) { return k * x + b; }
    	inline bool operator < (const Line &l) const { return k < l.k || (k == l.k && b < l.b); }
    };
    inline double crs(const Line &l1, const Line &l2) { return ((double)l2.b - l1.b) / (l1.k - l2.k); }
    
    struct Convex {
    	Line a[B], q[B];
    	int n, tl, now;
    	
    	inline void build() {
    		std::sort(a + 1, a + n + 1);
    		tl = 0;
    		for (int i = 1; i <= n; ++i) {
    			while (tl > 1 && crs(q[tl - 1], a[i]) <= crs(q[tl - 1], q[tl])) --tl;
    			q[++tl] = a[i];
    		}
    	}
    	inline void reset(int nn) { now = 1, n = nn; }
    	inline ll get(int p) {
    		while (now <= tl) {
    			if (now == tl || p <= crs(q[now], q[now + 1])) return q[now].get_y(p);
    			else ++now;
    		}
    		return assert(0), 0;
    	}
    } c[B * B], spre[B * B], ssuf[B * B];
    
    inline void rebuild(int i, int l = 1, int r = 0, int k = 0) {
    	sum[i] = 0;
    	for (int j = st(i); j <= ed(i); ++j) a[j] += add[i], sum[i] += a[j];
    	add[i] = 0;
    	for (int j = l; j <= r; ++j) a[j] += k, sum[i] += k;
    	int len = ed(i) - st(i) + 1;
    	ll s1 = 0, s2 = 0;
    	c[i].reset(len), spre[i].reset(len), ssuf[i].reset(len);
    	for (int j = 1; j <= len; ++j) {
    		ll mx = -0x7fffffffffffffff, s = 0;
    		for (int k = st(i); k <= st(i) + j - 1; ++k) s += a[k];
    		mx = s;
    		for (int k = st(i) + 1; k <= ed(i) - j + 1; ++k) smax(mx, s += a[k + j - 1] - a[k - 1]);
    		s1 += a[st(i) + j - 1], s2 += a[ed(i) - j + 1];
    		c[i].a[j].k = j, c[i].a[j].b = mx;
    		spre[i].a[j].k = j, spre[i].a[j].b = s1;
    		ssuf[i].a[j].k = j, ssuf[i].a[j].b = s2;
    	}
    	c[i].build(), spre[i].build(), ssuf[i].build();
    }
    inline void build() {
    	for (int i = 1; i <= bl(n); ++i) rebuild(i);
    }
    inline void qadd(int l, int r, int k) {
    	if (bl(l) == bl(r)) return rebuild(bl(l), l, r, k);
    	for (int i = bl(l) + 1; i < bl(r); ++i) add[i] += k, sum[i] += k * (ed(i) - st(i) + 1ll);
    	rebuild(bl(l), l, ed(bl(l)), k), rebuild(bl(r), st(bl(r)), r, k);
    }
    inline ll qans(int l, int r) {
    	if (bl(l) == bl(r)) {
    		ll ans = 0, s = 0, b = bl(l);
    		for (int i = l; i <= r; ++i) {
    			s = std::max(s + a[i] + add[b], (ll)a[i] + add[b]);
    			smax(ans, s);
    		}
    		return ans;
    	}
    	ll ans = 0, s = 0;
    	for (int i = l; i <= ed(bl(l)); ++i) s = std::max(s + a[i] + add[bl(i)], (ll)a[i] + add[bl(i)]), smax(ans, s);
    	for (int i = bl(l) + 1; i < bl(r); ++i) {
    		smax(ans, s + spre[i].get(add[i]));
    		smax(ans, c[i].get(add[i]));
    		s = std::max(s + sum[i], ssuf[i].get(add[i]));
    	}
    	for (int i = st(bl(r)); i <= r; ++i) s = std::max(s + a[i] + add[bl(i)], (ll)a[i] + add[bl(i)]), smax(ans, s);
    	return ans;
    }
    
    inline void work() {
    	build();
    	while (m--) {
    		int l, r, x;
    		static char s[5];
    		scanf("%s", s);
    		if (*s == 'Q') {
    			read(l), read(r);
    			printf("%lld
    ", qans(l, r));
    		} else read(l), read(r), read(x), qadd(l, r, x);
    	}
    }
    
    inline void init() {
    	read(n), read(m);
    	blo = pow(n, 1.0 / 3);;
    	for (int i = 1; i <= n; ++i) read(a[i]);
    }
    
    int main() {
    #ifdef hzhkk
    	freopen("hkk.in", "r", stdin);
    #endif
    	init();
    	work();
    	fclose(stdin), fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    spring cash 使用详解
    集合流式编程
    存储过程入门
    SpringBoot 项目鉴权的 4 种方式
    kafka使用详解
    Lambda表达式详解
    TypeError: Cannot read property 'concat' of undefined
    spring cloud 跨maven module调用方法
    vue axios嵌套for中调用axios async await
    vue 跨页面传递数组参数
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj5089.html
Copyright © 2020-2023  润新知