码迷,mamicode.com
首页 > 其他好文 > 详细

序列DDP

时间:2019-12-31 23:20:30      阅读:118      评论:0      收藏:0      [点我收藏+]

标签:维护   输入   ++   hup   define   ber   turn   表示   位置   

一个题

一个序列,有两种操作。第一种操作是修改一个位置的数,第二种操作是求一个区间的价值。
一段区间的价值的定义如下:可以在这段区间内取任意多个位置的数,但是这些数的位置不能相邻,这些数的和的最大值为这段区间的价值。
如有序列(1,-1,-2,3,4,2,-1),则区间[4,6]的价值为5。
1s内能承受的最大复杂度为O(nlogn)

第一行输入 n, m
接下来一行n个数
接下来m行操作
每个操作形如o, a, b
如果o=1,则为修改
如果o=2,则为查询


超sb \(nm\log n\) 比暴力还慢

二分 + 合并

二分时维护左右区间:
选最左侧 不选最右侧时 区间和
不选最左侧 选最右侧时 区间和
都选时 区间和
都不选时 区间和

递归合并

//
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define max std::max
const int MARX = 1e5 + 10;
//===========================================================
int N, M, number[MARX];
//===========================================================
inline int read()
{
    int f = 1, w = 0; char ch = getchar();
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
    return f * w;
}
void Query(int L, int R, int &l, int &r, int &no, int &all)
{
    if(L == R) {l = r = all = number[L]; no = 0; return ;}
    int mid = (L + R) >> 1;
    int ll = 0, rl = 0, nol = 0, alll = 0, lr = 0, rr = 0, nor = 0, allr = 0;
    Query(L, mid, ll, rl, nol, alll);
    Query(mid + 1, R, lr, rr, nor, allr);
    
    l = ll + nor;
    if(L != mid) l = max(l, ll + lr);
    l = max(l, alll + nor);
    
    r = nol + rr;
    if(R != mid + 1) r = max(r, rl + rr);
    r = max(r, nol + allr);
    
    no = nol + nor;
    if(R != mid + 1) no = max(no, nol + lr);
    if(L != mid) no = max(no, rl + nor);
    
    if(R != mid + 1) all = alll + rr;
    if(L != mid) if(! all)all = ll + allr; else all = max(all, ll + allr);
    if(R != mid + 1 && L != mid) all = max(all, ll + rr);
}
//===========================================================
int main()
{
    N = read(), M = read();
    for(int i = 1; i <= N; i ++) number[i] = read();
    
    while(M --)
    {
      int opt = read(), L, R, Pos, Val;
      if(opt == 1) {Pos = read(), Val = read(); number[Pos] = Val;}
      else 
      {
        L = read(), R = read();
        int l, r, no, all; 
        Query(L, R, l, r, no, all);
        printf("%d\n", max(l , max(r, max(no, all))));
      }
    }
    return 0;
}
/*
7 8
1 -1 -2 3 4 2 -1
1 1 1
1 2 1
1 3 1
1 4 1
1 5 1
1 6 1
1 7 114514
2 1 7
*/

暴力:

f[i][0] 表示第i位不选时 最大和
f[i][1] 表示第i位选择时 最大和

有: f[i][0] = max(f[i-1][0],f[i-1][1])
f[i][1] = f[i - 1][0] + a[i]

每次询问都全部更新一次DP数组 , 复杂度 \(O(nm)\)


正解:

重定义矩阵乘法, 设\(c_{(i,j)} = \max(a_{(i,j)} +b_{(k,j)})\)

则有:
\(\begin{bmatrix}f[i - 1][1]&f[i - 1][0]\end{bmatrix} \times \begin{bmatrix}-\inf&0\\a[i]&0\end{bmatrix}\)

发现依然满足矩阵乘法的 结合律
搞个DDP随便维护一下就过了
\(O(m\log n)\)

#include <cstdio>
#include <cstring>
#include <algorithm>
const int maxn = 100010;
const int inf = 0x3f3f3f3f;

int a[maxn];

struct mat {
  int a[2][2];
  mat() { memset(a, 0, sizeof a); }
  mat operator * (const mat &rhs) const {
    mat ans;
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < 2; ++j) {
        int res = -inf;
        for (int k = 0; k < 2; ++k) {
          res = std::max(res, a[i][k] + rhs.a[k][j]);
        }
        ans.a[i][j] = res;
      }
    }
    return ans;
  }
};

struct seg {
  int l, r;
  mat sum;
} t[maxn << 2];

#define lch (id << 1)
#define rch (id << 1 | 1)

void inline pushup(int id) {
  t[id].sum = t[lch].sum * t[rch].sum;
}

void build(int id, int l, int r) {
  t[id].l = l, t[id].r = r;
  if (l == r) return (void)(t[id].sum.a[0][0] = -inf, t[id].sum.a[1][0] = a[l]);
  int mid = (l + r) >> 1;
  build(lch, l, mid); build(rch, mid + 1, r);
  pushup(id);
}

void mdy(int id, int p, int v) {
  if (t[id].l == t[id].r) return (void)(t[id].sum.a[1][0] = v);
  int mid = (t[id].l + t[id].r) >> 1;
  mdy(p <= mid ? lch : rch, p, v);
  pushup(id);
}

mat qry(int id, int l, int r) {
  if (t[id].l == l && t[id].r == r) return t[id].sum;
  int mid = (t[id].l + t[id].r) >> 1;
  if (r <= mid) return qry(lch, l, r);
  else if (l > mid) return qry(rch, l, r);
  else return qry(lch, l, mid) * qry(rch, mid + 1, r);
}

void mdy(int p, int v) {
  mdy(1, p, v);
}

int qry(int l, int r) {
  mat res = qry(1, l, r);
  return std::max(std::max(res.a[0][0], res.a[1][0]), std::max(res.a[0][1], res.a[1][1]));
}

int main() {
  int n, m, o, l, r;
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i) scanf("%d", a + i);
  build(1, 1, n);
  while (m--) {
    scanf("%d%d%d", &o, &l, &r);
    if (o == 1) mdy(l, r);
    else printf("%d\n", qry(l, r));
  }
  return 0;
}

序列DDP

标签:维护   输入   ++   hup   define   ber   turn   表示   位置   

原文地址:https://www.cnblogs.com/luckyblock/p/12127669.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!