zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    使用CustomValidate自定义验证控件
    C#中金额的大小写转换
    Andriod出错之Unable to build: the file dx.jar was not loaded from the SDK folder!
    VC 编写的打字练习
    机房工作笔记Ping只有单向通
    web服务协同学习笔记(1)
    Dll 学习3 将MDI子窗口封装在DLL中
    机房工作学习文件共享
    Andriod出错之Failed to find an AVD compatible with target 'Android 2.2'
    Andriod出错之wrapper was not properly loaded first
  • 原文地址:https://www.cnblogs.com/hankeke/p/bzoj5089.html
Copyright © 2011-2022 走看看