bzoj4504 K个串

K个串

题目背景:

bzoj4503

分析:

首先考虑如果没有负数,那么我们就可以直接从段的长度从大到小枚举就可以了,然而,这显然不能A题,我们考虑对于每一个位置i,我们维护以它为右端点的每一个子串的和,并且令maxp[i]为当前的这一部分的子串和的最大值,显然,全局的最大值,就是所有maxp[i]的最大值,然后我们用当前最大值所在的i的以其为右端点的子串中的次大值来重新更新,那么将这些操作重复k次即可以得到答案。

然后我们来考虑如何维护c[i],以及在每次更新后,重新找出次大值,首先我们可以发现,如果我们把以i为右端点的所有子串的和求取出来后,我们求取以i + 1为右端点的子串和则只需要,将[last[num[i + 1]] + 1, i + 1]这一段区间的和全部加上num[i + 1]即可,那么我们只需要维护上一个num[i + 1]的出现位置(last数组)即可

然后,对于每一次询问全局的最大值,我们只需要开一个堆即可,然后对于如何找出次大值,并且去除最大值的影响,我们可以选择进行进行的操作是,首先记录maxp,及其对应的i,以及当前的maxp由哪一段区间得到(如一开始都是由[1,i]得到,所以存入1i),还有这一个maxp是以哪一位置为左端点,然后我们每一次取出最大值之后,假设他是maxp[x],且以y为左端点,且由l, r询问得来,那么我们只需要将以当前x为根的,区间[l, y – 1], [y + 1, r]中的询问得到的最大值都放进去即可。

注意:对于每一次由上一个线段树区间加得到下一个,我们可以选择两种方法,标记永久化,或者是标记下方,后者的实现较为麻烦,且容易出现错误,所以这里推荐前者,每一次只需要将标记节点新建即可。

Source

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #include 
         
           #include 
           #include 
           
             using namespace std; inline char read() { static const int IN_LEN = 1024 * 1024; static char buf[IN_LEN], *s, *t; if (s == t) { t = (s = buf) + fread(buf, 1, IN_LEN, stdin); if (s == t) return -1; } return *s++; } template 
            
              inline bool R(T &x) { static char c; static bool iosig; for (c = read(), iosig = false; !isdigit(c); c = read()) { if (c == -1) return false; if (c == '-') iosig = true; } for (x = 0; isdigit(c); c = read()) x = (x << 3) + (x << 1) + (c ^ '0'); if (iosig) x = -x; return true; } const int OUT_LEN = 1024 * 1024; char obuf[OUT_LEN], *oh = obuf; inline void writechar(char c) { if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf; *oh++ = c; } template 
             
               inline void W(T x) { static int buf[30], cnt; if (!x) writechar(48); else { if (x < 0) writechar('-'), x = -x; for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48; while (cnt) writechar(buf[cnt--]); } } inline void flush() { fwrite(obuf, 1, oh - obuf, stdout); } const int MAXN = 100000 + 10; const int MAXK = 200000 + 10; int n, k, num[MAXN]; map 
              
                last; struct node { node *left, *right; long long max, lazy; int id; } *root[MAXN], pool[(MAXN + MAXK) << 4], *cur = pool; inline void build(int l, int r, node *&x) { x = ++cur, x->id = l; if (l == r) return ; int mid = l + r >> 1; build(l, mid, x->left), build(mid + 1, r, x->right); } inline void insert(node *x, node *&y, int l, int r, int ql, int qr, long long num) { y = ++cur; y->left = x->left, y->right = x->right, y->max = x->max; y->lazy = x->lazy, y->id = x->id; if (ql <= l && r <= qr) return (void)(y->lazy += num, y->max += num); int mid = l + r >> 1; if (ql <= mid) insert(x->left, y->left, l, mid, ql, qr, num); if (qr > mid) insert(x->right, y->right, mid + 1, r, ql, qr, num); if (y->left->max > y->right->max) y->max = y->left->max + y->lazy, y->id = y->left->id; else y->max = y->right->max + y->lazy, y->id = y->right->id; } struct temp { int id; long long max; temp(int id, long long max) : id(id), max(max) {} temp() {} }; inline temp query(node *rt, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) return temp(rt->id, rt->max); int mid = l + r >> 1; if (qr <= mid) { temp tem = query(rt->left, l, mid, ql, qr); tem.max += rt->lazy; return tem; } else if (ql > mid) { temp tem = query(rt->right, mid + 1, r, ql, qr); tem.max += rt->lazy; return tem; } else { temp temp1 = query(rt->left, l, mid, ql, qr); temp temp2 = query(rt->right, mid + 1, r, ql, qr); return (temp1.max > temp2.max) ? (temp1.max += rt->lazy, temp1) : (temp2.max += rt->lazy, temp2); } } struct data { int i, j, l, r; long long max; data(int i, int j, int l, int r, long long max) : i(i), j(j), l(l), r(r), max(max) {} data() {} bool operator < (const data &a) const { return max < a.max; } }; priority_queue 
                q; inline void pre() { R(n), R(k); build(1, n, root[0]); /*初始化root[0]*/ for (int i = 1; i <= n; ++i) { R(num[i]); insert(root[i - 1], root[i], 1, n, last[num[i]] + 1, i, num[i]); /*运用上一个根的信息,和last数组进行更新*/ temp tem = query(root[i], 1, n, 1, i); q.push(data(i, tem.id, 1, i, tem.max)); last[num[i]] = i; /*因为数据过大,利用map来查找上一次的出现位置*/ } } inline void work() { for (int i = 1; i < k; ++i) { /*取出最大值,并推入两次大值*/ data tem = q.top(); q.pop(); if (tem.j > tem.l) { /*区间[l, y - 1]*/ temp t = query(root[tem.i], 1, n, tem.l, tem.j - 1); q.push(data(tem.i, t.id, tem.l, tem.j - 1, t.max)); } if (tem.j < tem.r) { /*区间[y + 1, r]*/ temp t = query(root[tem.i], 1, n, tem.j + 1, tem.r); q.push(data(tem.i, t.id, tem.j + 1, tem.r, t.max)); } } W(q.top().max); } int main() { //freopen("in.in", "r", stdin); pre(); work(); flush(); return 0; } 
               
              
             
            
          
         
       
      
      
     
     
    
    
   
   

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值