SPOJ GSS3 (动态dp)

题意

题目链接

Sol

这题可以动态dp做。

设\(f[i]\)表示以\(i\)为结尾的最大子段和,\(g[i]\)表示\(1-i\)的最大子段和

那么

\(f[i] = max(f[i - 1] + a[i], a[i])\)

\(g[i] = max(g[i - 1], f[i])\)

发现只跟前一项有关,而且\(g[i]从\)f[i]$转移过来的那一项可以直接拆开

那么构造矩阵

\[
\begin{bmatrix}
a_{i} & -\infty & \dots a_{i} \\
a_{i}, & 0 & a_{i}\\
-\infty, & -\infty & 0 \\
\end{bmatrix}
\]

直接转移就行了

复杂度\(O(nlogn * 27)\)

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 10, INF = 1e9;
template<typename A, typename B> inline bool chmax(A &x, B y) {return x < y ? x = y, 1 : 0;}
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
struct Ma {
    int m[4][4];
    Ma() {
        memset(m, -0x3f, sizeof(m));
    }
    Ma operator * (const Ma &rhs) const {
        Ma ans;
        for(int i = 1; i <= 3; i++)
            for(int j = 1; j <= 3; j++)
                for(int k = 1; k <= 3; k++)
                    chmax(ans.m[i][j], m[i][k] + rhs.m[k][j]);
        return ans;
    }
    void init(int v) {
        m[1][1] = v; m[1][2] = -INF; m[1][3] = v;
        m[2][1] = v; m[2][2] = 0;    m[2][3] = v;
        m[3][1] = -INF; m[3][2] = -INF; m[3][3] = 0;
    }
}m[MAXN];
int N, M, a[MAXN];
#define ls k << 1
#define rs k << 1 | 1
void update(int k) {
    m[k] = m[ls] * m[rs];
}
void Build(int k, int l, int r) {
    if(l == r) {m[k].init(a[l]); return ;}
    int mid = l + r >> 1;
    Build(ls, l, mid); Build(rs, mid + 1, r);
    update(k);
}
void Modify(int k, int l, int r, int p, int v) {
    if(l == r) {m[k].init(v); return ;}
    int mid = l + r >> 1;
    if(p <= mid) Modify(ls, l, mid, p, v);
    else Modify(rs, mid + 1, r, p, v);
    update(k);
}
Ma Query(int k, int l, int r, int ql, int qr) {
    if(ql <= l && r <= qr)
        return m[k];
    int mid = l + r >> 1;
    if(ql > mid) return Query(rs, mid + 1, r, ql, qr);
    else if(qr <= mid) return Query(ls, l, mid, ql, qr);
    else return (Query(ls, l, mid, ql, qr) * Query(rs, mid + 1, r, ql, qr));
}
int main() {
    N = read();
    for(int i = 1; i <= N; i++) a[i] = read();
    Build(1, 1, N);
    M = read();
    while(M--) {
        int opt = read(), x = read(), y = read();
        if(opt == 0) Modify(1, 1, N, x, y);
        else {
            Ma ans = Query(1, 1, N, x, y);
            printf("%d\n", max(ans.m[2][1], ans.m[2][3]));
        }
    }
    return 0;
}
/*
4
-1 -2 -3 -4
2
1 1 4
1 1 2
*/

原文地址:https://www.cnblogs.com/zwfymqz/p/10425320.html

时间: 2024-10-01 00:37:46

SPOJ GSS3 (动态dp)的相关文章

SPOJ GSS3 Can you answer these queries III (线段树)

题目大意: 求区间最大子区间的和. 思路分析: 记录左最大,右最大,区间最大. 注意Q_L  和 Q_R  就好. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define lson num<<1,s,mid #define rson num<<1|1,mid+1,e #define maxn 55555 using na

SPOJ ANARC05H 计数DP

给定一个数字串,问有多少种拆分方法,题目所谓的拆分,就是分成若干个子块,每个块的和 即为各个数字相加,当前块的和一定要小于等于后面的块的和 比如1117  就有这些[1-117], [1-1-17], [1-11-7], [1-1-1-7], [11-17],and [111-7] 肯定是计数DP,而且二维即可,不过第二维应该怎么设置是亮点,我也想了好多种方案,不过都被否定了,后来还是一种其实比较经典的方案进来了,就是代表当前最后一个块的和是多少,则当前dp[i][j] 由dp[i-1][k]转

4712: 洪水 基于链分治的动态DP

国际惯例的题面:看起来很神的样子......如果我说这是动态DP的板子题你敢信?基于链分治的动态DP?说人话,就是树链剖分线段树维护DP.既然是DP,那就先得有转移方程.我们令f[i]表示让i子树中的叶子节点全部与根不联通,所需要的最小代价,v[i]为输入的点权.显然f[i]=min(v[i],sigma(f[soni])),边界条件是,如果i是叶子节点,则f[i]=v[i].我们需要用链分治去维护这个DP,所以要把DP拆成重链和轻链独立的形式.我们还是用f[i]表示让i子树中的叶子节点全部与根

[动态dp]线段树维护转移矩阵

背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区间,直接列出整个区间的dp方程.这个是基础,动态dp无论如何还是dp(这一步是一般是重点) 2.列出转移矩阵.由于有很多修改操作,我们将数据集中在一起处理,还可以利用矩阵结合律,并且区间比较好提取,(找一段矩阵就好了),修改也方便. 3.线段树维护矩阵.对于修改,我们就是在矩阵上进行修改,对于不同的

UOJ268 [清华集训2016] 数据交互 【动态DP】【堆】【树链剖分】【线段树】

题目分析: 不难发现可以用动态DP做. 题目相当于是要我求一条路径,所有与路径有交的链的代价加入进去,要求代价最大. 我们把链的代价分成两个部分:一部分将代价加入$LCA$之中,用$g$数组保存:另一部分将代价加在整条链上,用$d$数组保存. 这时候我们可以发现,一条从$u$到$v$的路径的代价相当于是$d[LCA(u,v)]+\sum_{x \in edge(u,v)}g[x]$. 如果是静态的,可以用树形DP解决. 看过<神奇的子图>的同学都知道,叶子结点是从它的儿子中取两个最大的出来,所

bzoj 4712 洪水——动态DP

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4712 因为作为动态DP练习而找到,所以就用动态DP做了,也没管那种二分的方法. 感觉理解似乎加深了. 果然初始权值也都是非负的. 所以 dp[cr] 表示当前子树与自己的叶子都断开了的最小代价,则 dp[cr]=min{ sigma dp[v] , w[cr] }(v是cr的直接孩子). 但这样的话,修改的时候需要把自己到根的路径都走一遍.不过查询是O(1)的,所以考虑分配一下. 走到根的

uoj#268. 【清华集训2016】数据交互(动态dp+堆)

传送门 动态dp我好像还真没咋做过--通过一个上午的努力光荣的获得了所有AC的人里面的倒数rk3 首先有一个我一点也不觉得显然的定理,如果两条路径相交,那么一定有一条路径的\(LCA\)在另一条路径上 于是我们可以对于每一个点记录两个值,一个\(a_i\)表示\(LCA\)在\(i\)点的所有路径的权值之和,一个是\(b_i\),表示经过点\(i\)且\(LCA\)不在点\(i\)的所有路径的权值之和 那么对于一条路径\((u,v)\),它的权值就是\(b_{LCA(u,v)}+\sum_{i\

回文串 --- 动态dp UVA 11584

题目链接: https://cn.vjudge.net/problem/34398/origin 本题的大意其实很简单,就是找回文串,大致的思路如下: 1. 确定一个回文串,这里用到了自定义的check函数原理如下: 传入le, ri两个值(定义从1开始), s+1 = aaadbccb. a a a d b c c b 1 2 3 4 5 6 7 8 比如,le = 5, ri = 8. 则s[5] == s[8]成立 le++ ri-- 再比较 s[6] == s[7]? 成立 le++,

LG4719 【模板】动态dp

题意 题目描述 给定一棵\(n\)个点的树,点带点权. 有\(m\)次操作,每次操作给定\(x,y\),表示修改点\(x\)的权值为\(y\). 你需要在每次操作之后求出这棵树的最大权独立集的权值大小. 输入输出格式 输入格式: 第一行,\(n,m\),分别代表点数和操作数. 第二行,\(V_1,V_2,...,V_n\),代表\(n\)个点的权值. 接下来\(n-1\)行,\(x,y\),描述这棵树的\(n-1\)条边. 接下来\(m\)行,\(x,y\),修改点\(x\)的权值为\(y\).