SP1716 GSS3 - Can you answer these queries III

题面

题解

相信大家写过的传统做法像这样:(这段代码蒯自Karry5307的题解

struct SegmentTree{
    ll l,r,prefix,suffix,sum,maxn;  
};

//...

inline void update(ll node)
{
    ll res;
    tree[node].sum=tree[node<<1].sum+tree[(node<<1)|1].sum;
    tree[node].maxn=max(tree[node<<1].maxn,tree[(node<<1)|1].maxn);
    res=tree[node<<1].suffix+tree[(node<<1)|1].prefix;
    tree[node].maxn=max(tree[node].maxn,res);
    res=tree[node<<1].sum+tree[(node<<1)|1].prefix;
    tree[node].prefix=max(tree[node<<1].prefix,res);
    res=tree[node<<1].suffix+tree[(node<<1)|1].sum;
    tree[node].suffix=max(tree[(node<<1)|1].suffix,res);
}

//...

有没有觉得这种做法有些麻烦

这里将一种硬核做法:动态dp

这个部分参考了GKxx 的博客

引入广义矩阵乘法:

\[\mathrm{C} = \mathrm{A} * \mathrm{B} \Leftrightarrow \mathrm{C}_{i,j} = \max_k\left\{\mathrm{A}_{i,k} + \mathrm{B}_{k,j}\right\} \]

这样的话,我们首先写出动态规划的柿子:

\(f_i\)表示以\(i\)结尾的最大子段和,\(g_i\)表示\([1,i]\)的最大子段和

于是

\[\begin{aligned} f_i &= \max(f_{i-1} + a_i, a_i) \\ g_i &= \max(g_{i-1}, f_i) \end{aligned} \]

欢乐地写出矩乘的柿子:

\[\begin{bmatrix}f_{i-1} & g_{i-1} & 0\end{bmatrix} \times\begin{bmatrix}a_i & a_i & -\infty\\-\infty & 0 & -\infty\\a_i & a_i & 0\end{bmatrix}=\begin{bmatrix}f_i & g_i & 0\end{bmatrix} \]

妙哉

因为矩阵乘法具有结合律,于是可以用线段树维护

当然资瓷单点修改和查询区间最大子段和了

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define file(x) freopen(#x".in", "r", stdin);freopen(#x".out", "w", stdout);
#define clear(x, y) memset(x, y, sizeof(x));

inline int read()
{
	int data = 0, w = 1;
	char ch = getchar();
	while(ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int maxn(50010), INF(0x3f3f3f3f);
template<typename T> inline void chkmax(T &a, const T &b)
	{ return (void) (a < b ? a = b : 0); }
struct Matrix
{
	int a[3][3];
	inline int *operator [] (const int &x) { return a[x]; }
	inline const int *operator [] (const int &x) const { return a[x]; }
} mat[maxn << 2]; int n, Q, a[maxn];

inline Matrix operator * (const Matrix &a, const Matrix &b)
{
	Matrix c; for(int i = 0; i < 3; i++) c[i][0] = c[i][1] = c[i][2] = -INF;
	for(int i = 0; i < 3; i++)
		for(int j = 0; j < 3; j++)
			for(int k = 0; k < 3; k++)
				chkmax(c[i][k], a[i][j] + b[j][k]);
	return c;
}

void build(int root = 1, int l = 1, int r = n)
{
	if(l == r)
	{
		Matrix &o = mat[root]; o[0][1] = o[2][0] = o[2][1] = -INF;
		o[0][0] = o[0][2] = o[1][0] = o[1][2] = a[l];
		o[1][1] = o[2][2] = 0; return;
	}
	int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
	build(lson, l, mid), build(rson, mid + 1, r);
	mat[root] = mat[lson] * mat[rson];
}

void update(int id, int v, int root = 1, int l = 1, int r = n)
{
	if(l == r) return (void)
		(mat[root][0][0] = mat[root][0][2]
		 = mat[root][1][0] = mat[root][1][2] = v);
	int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
	if(id <= mid) update(id, v, lson, l, mid);
	else update(id, v, rson, mid + 1, r);
	mat[root] = mat[lson] * mat[rson];
}

Matrix query(int ql, int qr, int root = 1, int l = 1, int r = n)
{
	if(ql <= l && r <= qr) return mat[root];
	int mid = (l + r) >> 1, lson = root << 1, rson = lson | 1;
	if(qr <= mid) return query(ql, qr, lson, l, mid);
	if(ql >  mid) return query(ql, qr, rson, mid + 1, r);
	return query(ql, qr, lson, l, mid) * query(ql, qr, rson, mid + 1, r);
}

int main()
{
	n = read();
	for(RG int i = 1; i <= n; i++) a[i] = read();
	build(); Q = read();
	while(Q--)
	{
		int opt = read(), x = read(), y = read();
		if(opt)
		{
			Matrix ans = query(x, y);
			printf("%d\n", std::max(ans[1][0], ans[1][2]));
		}
		else a[x] = y, update(x, y);
	}
	return 0;
}
posted @ 2019-02-03 18:47  xgzc  阅读(217)  评论(2编辑  收藏  举报