SPOJ1557 GSS2 Can you answer these queries II 历史最值线段树

传送门

题意:给出一个长度为$N$的数列,$Q$次询问,每一次询问$[l,r]$之间的最大子段和,相同的数只计算一次。所有数字的绝对值$\leq 10^5$


GSS系列中不板子的大火题,单独拿出来写

因为相同的数字只计算一次,像GSS1中的合并操作就无法进行,传统做法失效,我们需要一种更强大的做法。

考虑到去重,与HH的项链很相似,所以考虑离线、对询问以$r$从小到大进行排序后进行计算。

考虑到每一次$r$的增加都会产生新的可能的最大子段和,我们用如下方式维护线段树:对于第$i$个叶子节点,它包含两个元素:$sum$表示$\sum \limits_i^r num_i$(不算重复元素),$hisMax$表示$\sum \limits_i^r num_i$在曾经的过程中取到的最大值(也就是左端点为$l$,右端点在$[l,r]$之间的最大子段和)。而对于每一个非叶子节点,它的$sum$和$hisMax$都取其左右儿子的最大值。这样每一次询问操作只需要询问$[l,r]$的$hisMax$即可。

对于$r$的右移操作,设$pre_i$表示数字$i$最后一次出现的位置,将右端点从$r$移到$r+1$的过程就是对$pre_{num_{r+1}}$到$r+1$的所有位置加上$num_{r+1}$的操作。

考虑到复杂度,所以我们需要用到懒标记,而pushdown在其中是十分讲究的,具体代码和解释在下面

($h\_tag$表示$hisMax$的标记(相当于在当前$hisMax$还需要加多少),$s\_tag$表示$sum$的懒标记)

inline void pushdown(int now){
    if(Tree[now].h_tag){
        Tree[lch].hisMax = max(Tree[lch].hisMax , Tree[lch].sum + Tree[now].h_tag);
        Tree[rch].hisMax = max(Tree[rch].hisMax , Tree[rch].sum + Tree[now].h_tag);
        Tree[lch].h_tag = max(Tree[lch].h_tag , Tree[lch].s_tag + Tree[now].h_tag);
        Tree[rch].h_tag = max(Tree[rch].h_tag , Tree[rch].s_tag + Tree[now].h_tag);
/*
h_tag和hisMax实质上都是前缀最大值
在一个后缀加入的时候(也就是Tree[now].h_tag传下来的时候),s_tag和sum可以跟当前的h_tag接上变成一个新的前缀
而s_tag与h_tag两个标记对应的区间的左端点一定是一致的,sum和hisMax显然是一致的,于是h_tag和hisMax可以这样转移
*/ Tree[now].h_tag = 0; } if(Tree[now].s_tag){ Tree[lch].sum += Tree[now].s_tag; Tree[rch].sum += Tree[now].s_tag; Tree[lch].s_tag += Tree[now].s_tag; Tree[rch].s_tag += Tree[now].s_tag; Tree[now].s_tag = 0; }//这个没什么好说的,基本的线段树操作 } //注意一定要先传h_tag再传s_tag,因为h_tag和hisMax传下来的时候是与之前的那一段进行连接,而不是与当前计算的这一段进行连接

完整代码:

  1 #include<bits/stdc++.h>
  2 #define lch (now << 1)
  3 #define rch (now << 1 | 1)
  4 #define mid ((l + r) >> 1)
  5 #define int long long
  6 //This code is written by Itst
  7 using namespace std;
  8 
  9 inline int read(){
 10     int a = 0;
 11     bool f = 0;
 12     char c = getchar();
 13     while(c != EOF && !isdigit(c)){
 14         if(c == '-')
 15             f = 1;
 16         c = getchar();
 17     }
 18     while(c != EOF && isdigit(c)){
 19         a = (a << 3) + (a << 1) + (c ^ '0');
 20         c = getchar();
 21     }
 22     return f ? -a : a;
 23 }
 24 
 25 const int MAXN = 100010;
 26 struct node{
 27     int sum , hisMax , s_tag , h_tag;
 28 }Tree[MAXN << 2];
 29 struct query{
 30     int l , r , ind;
 31 }now[MAXN];
 32 map < int , int > appear;
 33 int N , Q , num[MAXN] , ans[MAXN];
 34 
 35 bool operator <(query a , query b){
 36     return a.r < b.r;
 37 }
 38 
 39 inline void pushup(int now){
 40     Tree[now].sum = max(Tree[lch].sum , Tree[rch].sum);
 41     Tree[now].hisMax = max(Tree[lch].hisMax , Tree[rch].hisMax);
 42 }
 43 
 44 inline void pushdown(int now){
 45     if(Tree[now].h_tag){
 46         Tree[lch].hisMax = max(Tree[lch].hisMax , Tree[lch].sum + Tree[now].h_tag);
 47         Tree[rch].hisMax = max(Tree[rch].hisMax , Tree[rch].sum + Tree[now].h_tag);
 48         Tree[lch].h_tag = max(Tree[lch].h_tag , Tree[lch].s_tag + Tree[now].h_tag);
 49         Tree[rch].h_tag = max(Tree[rch].h_tag , Tree[rch].s_tag + Tree[now].h_tag);
 50         Tree[now].h_tag = 0;
 51     }
 52     if(Tree[now].s_tag){
 53         Tree[lch].sum += Tree[now].s_tag;
 54         Tree[rch].sum += Tree[now].s_tag;
 55         Tree[lch].s_tag += Tree[now].s_tag;
 56         Tree[rch].s_tag += Tree[now].s_tag;
 57         Tree[now].s_tag = 0;
 58     }
 59 }
 60 
 61 void modify(int now , int l , int r , int L , int R , int add){
 62     if(l >= L && r <= R){
 63         Tree[now].s_tag += add;
 64         Tree[now].sum += add;
 65         Tree[now].hisMax = max(Tree[now].hisMax , Tree[now].sum);
 66         Tree[now].h_tag = max(Tree[now].h_tag , Tree[now].s_tag);
 67         return;
 68     }
 69     pushdown(now);
 70     if(mid >= L)
 71         modify(lch , l , mid , L , R , add);
 72     if(mid < R)
 73         modify(rch , mid + 1 , r , L , R , add);
 74     pushup(now);
 75 }
 76 
 77 int query(int now , int l , int r , int L , int R){
 78     if(l >= L && r <= R)
 79         return Tree[now].hisMax;
 80     pushdown(now);
 81     int maxN = 0;
 82     if(mid >= L)
 83         maxN = query(lch , l , mid , L , R);
 84     if(mid < R)
 85         maxN = max(maxN , query(rch , mid + 1 , r , L , R));
 86     return maxN;
 87 }
 88 
 89 signed main(){
 90 #ifndef ONLINE_JUDGE
 91     freopen("1557.in" , "r" , stdin);
 92     //freopen("1557.out" , "w" , stdout);
 93 #endif
 94     N = read();
 95     for(int i = 1 ; i <= N ; ++i)
 96         num[i] = read();
 97     Q = read();
 98     for(int i = 1 ; i <= Q ; ++i){
 99         now[i].l = read();
100         now[i].r = read();
101         now[i].ind = i;
102     }
103     sort(now + 1 , now + Q + 1);
104     for(int i = 1 ; i <= Q ; ++i){
105         for(int j = now[i - 1].r + 1 ; j <= now[i].r ; ++j){
106             modify(1 , 1 , N , appear[num[j]] + 1 , j , num[j]);
107             appear[num[j]] = j;
108         }
109         ans[now[i].ind] = query(1 , 1 , N , now[i].l , now[i].r);
110     }
111     for(int i = 1 ; i <= Q ; ++i)
112         printf("%lld\n" , ans[i]);
113     return 0;
114 }

转载于:https://www.cnblogs.com/Itst/p/10042220.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值