SMOJ 2201 D (线段树)

题目描述

这是一道数据结构题。
我们拥有一个长度为n的数组a[i]。
我们有m次操作。操作有两种类型:
0 i val:表示我们要把a[i]修改为val;
1 l r k:表示我们要求出区间[l,r]的最多k个不相交子区间,并使得各个子区间的数的和尽量大,需要注意的是,我们也可以不选择区间,这时候数的和为0.
N,m不超过10^5.
所有的ai和val的绝对值均不超过500.k不超过20.询问的数目不超过10000.
所有输入保证合法。


输入格式

第一行为数据组数。
对于每一组数据:
第一行为正整数n。
第二行n个整数表示ai。
第三行一个正整数m。
接下来m行每行一个操作。具体看题面。


输出格式

对于每一组数据:
对于每一组询问,一行一个整数表示答案。


输入样例

1
9
9 -8 9 -1 -1 -1 9 -8 9
3
1 1 9 1
1 1 9 2
1 4 6 3


输出样例

17
25
0


Solution

线段树好题 + 码农级数据结构题!

比赛时想着如何将dp改成数据结构(明显是线段树),结果没想出来。后来才知道具体做法很简单。每次在区间中找一段最大的,并将这个区间取反,就是变成它的相反数。然后每个询问直接做k次就行了。

至于为什么想一下就知道了。假如这次贪心地取影响了后面的,那么后面取到它的相反数,就去除了影响。这样就保证贪心没有“后效性”了。这点我觉得有点像网络流里的反向边。很神奇哦。

虽说想法是naive的,但写法是要命的。这题我写了一个晚上写完了,第二天调了半个下午无果,丢下去打球,打完球回到机房后灵光一现,改了一下函数实现方式就立马过了!(得出结论:我太菜鸡了QAQ。。)

这题的基础模型是动态修改+求区间连续最大和。这是很经典的线段树模型,维护的东西有Lmax, Rmax, Tmax,sum等,比较多。这题由于要取反,还要记最大区间的左右端点,同样还要借助于左连续一段和右连续一段。于是还要记Lmaxp, Rmaxp, Tmaxp1, Tmaxp2,还有翻转标记rev。对于取反,我是开了两棵线段树,取反就Swap。总之要维护的东西很多,查询要用到的也不少,代码将近200行左右。比起LCT什么的,不算太长。但一直码不对,就有可能心态爆炸

需要注意的是,每次询问后记得取反回来之类的细节。


Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#define MAXN 100010
#define Max(a, b) ((a) > (b)) ? (a) : (b)
#define INF 1e9

using namespace std;

int nG;
int n, m;
int a[MAXN];

struct Tnode{
    int Lmax, Rmax, Tmax;
    int sum, Lmaxp, Rmaxp, Tmaxp1, Tmaxp2;
    bool rev;
    Tnode() {}
    Tnode(int _a, int _b, int _c, int _d, int _e, int _f, int _g, int _h, bool _i){
      Lmax = _a;
      Rmax = _b;
      Tmax = _c;
      sum = _d;
      Lmaxp = _e;
      Rmaxp = _f;
      Tmaxp1 = _g;
      Tmaxp2 = _h;
      rev = _i;
    }

}tree[2][MAXN<<2];

void Swap(Tnode& A, Tnode& B){
    Tnode C = A;  A = B;  B = C;
}


void Up(Tnode& root, Tnode& Lson, Tnode& Rson){
    root.sum = Lson.sum + Rson.sum;
    if(Lson.Lmax > Lson.sum + Rson.Lmax){
      root.Lmax = Lson.Lmax;
      root.Lmaxp = Lson.Lmaxp;
    }
    else{
      root.Lmax = Lson.sum + Rson.Lmax;
      root.Lmaxp = Rson.Lmaxp;
    }

    if(Rson.Rmax > Rson.sum + Lson.Rmax){
      root.Rmax = Rson.Rmax;
      root.Rmaxp = Rson.Rmaxp;
    }
    else{
      root.Rmax = Rson.sum + Lson.Rmax;
      root.Rmaxp = Lson.Rmaxp;
    }

    if(Lson.Tmax >= Rson.Tmax && Lson.Tmax >= Lson.Rmax + Rson.Lmax){
      root.Tmax = Lson.Tmax;
      root.Tmaxp1 = Lson.Tmaxp1;
      root.Tmaxp2 = Lson.Tmaxp2;
    }
    else if(Rson.Tmax >= Lson.Tmax && Rson.Tmax >= Lson.Rmax + Rson.Lmax){
      root.Tmax = Rson.Tmax;
      root.Tmaxp1 = Rson.Tmaxp1;
      root.Tmaxp2 = Rson.Tmaxp2;
    }
    else{
      root.Tmax = Lson.Rmax + Rson.Lmax;
      root.Tmaxp1 = Lson.Rmaxp;
      root.Tmaxp2 = Rson.Lmaxp;
    }
    root.rev = false;
}

void Down(int root, int Lson, int Rson){
    if(!tree[0][root].rev)  return;
    Swap(tree[0][Lson], tree[1][Lson]);
    tree[0][Lson].rev ^= 1;
    tree[1][Lson].rev ^= 1;
    Swap(tree[0][Rson], tree[1][Rson]);
    tree[0][Rson].rev ^= 1;
    tree[1][Rson].rev ^= 1;
}


void build(int root, int L, int R){
    if(L == R){

      for(int i = 0; i < 2; i++){
        if(!i)  tree[i][root].Tmax = tree[i][root].Rmax = tree[i][root].Lmax = tree[i][root].sum = a[L];
        else  tree[i][root].Tmax = tree[i][root].Rmax = tree[i][root].Lmax = tree[i][root].sum = -a[L];
        tree[i][root].Tmaxp1 = tree[i][root].Tmaxp2 = tree[i][root].Lmaxp = tree[i][root].Rmaxp = L;
        tree[i][root].rev = false;
      }
      return;
    }

    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
    build(Lson, L, mid);
    build(Rson, mid+1, R);
    Up(tree[0][root], tree[0][Lson], tree[0][Rson]);
    Up(tree[1][root], tree[1][Lson], tree[1][Rson]);
}


void update(int root, int L, int R, int x, int v){
    if(x > R || x < L)  return;
    if(L == x && x == R){
      tree[0][root].sum = v;
      tree[1][root].sum = -v;
      tree[0][root].Tmax = tree[0][root].Rmax = tree[0][root].Lmax = v;
      tree[1][root].Tmax = tree[1][root].Rmax = tree[1][root].Lmax = -v;
      return;
    }
    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
    Down(root, Lson, Rson);
    update(Lson, L, mid, x, v);
    update(Rson, mid+1, R, x, v);
    Up(tree[0][root], tree[0][Lson], tree[0][Rson]);
    Up(tree[1][root], tree[1][Lson], tree[1][Rson]);
}


void flip(int root, int L, int R, int x, int y){
    if(x > R || y < L)  return;
    if(x <= L && y >= R){
      Swap(tree[0][root], tree[1][root]);
      tree[0][root].rev ^= 1;
      tree[1][root].rev ^= 1;
      return;
    }

    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
    Down(root, Lson, Rson);
    flip(Lson, L, mid, x, y);
    flip(Rson, mid+1, R, x, y);
    Up(tree[0][root], tree[0][Lson], tree[0][Rson]);
    Up(tree[1][root], tree[1][Lson], tree[1][Rson]);
}

Tnode query(int root, int L, int R, int x, int y){
    if(x <= L && y >= R)  return tree[0][root];

    int mid = (L + R) >> 1, Lson = root << 1, Rson = root << 1 | 1;
    Down(root, Lson, Rson);
    tree[0][root].rev = tree[1][root].rev = false;
    if(y <= mid)  return query(Lson, L, mid, x, y);
    else if(x > mid)  return query(Rson, mid+1, R, x, y);
    else{
      Tnode temp;
      Tnode temp1 = query(Lson, L, mid, x, y);
      Tnode temp2 = query(Rson, mid+1, R, x, y);
      Up(temp, temp1, temp2);
      return temp;
    }
}

int main(){

    freopen("2201.in", "r", stdin);
    freopen("2201.out", "w", stdout);

    scanf("%d", &nG);
    while(nG --){
      scanf("%d", &n);
      for(int i = 1; i <= n; i++)  scanf("%d", &a[i]);
      build(1, 1, n);
      scanf("%d", &m);
      int a, b, c, d;
      for(int i = 1; i <= m; i++){
        scanf("%d%d%d", &a, &b, &c);
        if(!a)  update(1, 1, n, b, c);
        else{
          scanf("%d", &d);
          int ans = 0;
          Tnode here[25];
          for(int k = 1; k <= d; k++){
            here[k] = query(1, 1, n, b, c);
            if(here[k].Tmax > 0){
              ans += here[k].Tmax;
              flip(1, 1, n, here[k].Tmaxp1, here[k].Tmaxp2);
            }
          }
          for(int k = 1; k <= d; k++)
            if(here[k].Tmax > 0)  flip(1, 1, n, here[k].Tmaxp1, here[k].Tmaxp2);
          printf("%d\n", ans);
        }
      }
    }

    return 0;
}

这里写图片描述

之所以我不敢涉足
是因为回头亦是绝路

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值