题目:

Description

兔子们在玩k个串的游戏。首先,它们拿出了一个长度为n的数字序列,选出其中的一

个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)。

兔子们想知道,在这个数字序列所有连续的子串中,按照以上方式统计其所有数字之和,第

k大的和是多少。

Input

第一行,两个整数n和k,分别表示长度为n的数字序列和想要统计的第k大的和

接下里一行n个数a_i,表示这个数字序列

Output

一行一个整数,表示第k大的和

Sample Input

7 5

3 -2 1 2 2 1 3 -2

Sample Output

4

HINT

1 <= n <= 100000, 1 <= k <= 200000, 0 <= |a_i| <= 10^9数据保证存在第 k 大的和

题解:

首先我们发现,如果相同的字符的贡献可以重复计算的话,那就是超级钢琴了.

可以直接应用堆来求解

但是这道题中相同字符的贡献只统计一次,就需要我们瞎搞一下了。

对于这道题,我们可以通过可持久化线段树来处理去重的问题

怎么处理呢...

我们可以对每个点维护以这个点为右端点的所有子段和

这样我们考虑如何从i-1拓展到i

实际上我们拓展的时候只需要考虑第i位上的数字对前面做出的贡献

我们发现其实这个字符只对一部分左端点连续的字段做出贡献

具体一点,i位置上的数字只对左端点在\([last[a[i]]+1 ~ i]\)位置上的子段做出贡献

所以我们可以在可持久化线段树中通过区间修改实现\(nlogn\)处理出所有的子段

然后就是超级钢琴啦....

#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const ll maxn = 210010; struct Node{
Node *ch[2];
ll mx,id,lazy;
void update(){
mx = max(ch[0]->mx,ch[1]->mx);
if(mx == ch[0]->mx) id = ch[0]->id;
if(mx == ch[1]->mx) id = ch[1]->id;
mx += lazy;
}
}*null,*root[maxn],*it,mem[maxn*50];
inline void init(){
it = mem;null = it++;
null->ch[0] = null->ch[1] = null;
null->mx = null->id = 0;
root[0] = null;
}
Node* insert(Node *rt,ll l,ll r,ll L,ll R,ll val){
Node *p = it++;*p = *rt;
if(L == R && l == r){
p->id = l;
}
if(L <= l && r <= R){
p->lazy += val;
p->mx += val;
return p;
}
ll mid = l+r >> 1;
if(L <= mid) p->ch[0] = insert(p->ch[0],l,mid,L,R,val);
if(R > mid) p->ch[1] = insert(p->ch[1],mid+1,r,L,R,val);
p->update();return p;
} typedef pair<ll,ll> pa;
inline pa query(Node *p,ll l,ll r,ll L,ll R){
if(L <= l && r <= R){
return make_pair(p->mx,p->id);
}
ll mid = l+r >> 1;pa x,y;
if(R <= mid){
x = query(p->ch[0],l,mid,L,R);
x.first += p->lazy;
return x;
}
if(L > mid){
y = query(p->ch[1],mid+1,r,L,R);
y.first += p->lazy;
return y;
}
x = query(p->ch[0],l,mid,L,R);
y = query(p->ch[1],mid+1,r,L,R);
x.first += p->lazy;y.first+=p->lazy;
if(x.first > y.first) return x;
else return y;
}
ll a[maxn],last[maxn],b[maxn],c[maxn];
struct num{
ll l,r;
ll val,le,ri;
bool friend operator < (const num &a,const num &b){
return a.val < b.val;
}
num(){}
num(const ll &a,const ll &b,const ll &c,const ll &d,const ll &e){
l = a;r = b;val = c;le = d;ri = e;
}
};
priority_queue<num>q;
int main(){
init();
ll n,k;read(n);read(k);
for(ll i=1;i<=n;++i){
read(a[i]);
b[i] = a[i];
}
sort(b+1,b+n+1);
for(ll i=1;i<=n;++i){
c[i] = lower_bound(b+1,b+n+1,a[i]) - b;
}
memset(last,-1,sizeof last);
for(ll i=1;i<=n;++i){
b[i] = last[c[i]];
last[c[i]] = i;
}
for(ll i=1;i<=n;++i){
root[i] = insert(root[i-1],1,n,i,i,a[i]);
if(b[i] != -1){
ll l = b[i] + 1,r = i-1;
if(l <= r){
root[i] = insert(root[i],1,n,l,r,a[i]);
}
}else{
ll l = 1,r = i-1;
if(l <= r) root[i] = insert(root[i],1,n,l,r,a[i]);
}
pa x = query(root[i],1,n,1,i);
q.push(num(x.second,i,x.first,1,i));
}
while(--k){
num x = q.top();q.pop();
ll l = x.l + 1,r = x.ri;
if(l <= r){
pa tmp = query(root[x.r],1,n,l,r);
q.push(num(tmp.second,x.r,tmp.first,l,r));
}
l = x.le;r = x.l - 1;
if(l <= r){
pa tmp = query(root[x.r],1,n,l,r);
q.push(num(tmp.second,x.r,tmp.first,l,r));
}
}
num ans = q.top();
printf("%lld\n",ans.val);
getchar();getchar();
return 0;
}

如果你不会超级钢琴...

这道题要求k大值,实际上我们可以考虑依次求出第一大、第二大、第三大、 ... 、第k大

那么问题就在于如何求解这个东西。

我们可以采用如下的策略:

将所有的以某个点为右端点的所有向左拓展出来的子段中的最大子段和扔到堆里

每次从堆中取出当前子段和最大的子段,然后将这个子段的左区间向左向右延伸得到的最大的两个子段再扔进堆里

取出来的第i的即为第i大的子段和.

bzoj 4504: K个串 可持久化线段树+堆的更多相关文章

  1. 【BZOJ4504】K个串 可持久化线段树+堆

    [BZOJ4504]K个串 Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计 ...

  2. hihocoder#1046 K个串 可持久化线段树 + 堆

    首先考虑二分,然后发现不可行.... 注意到\(k\)十分小,尝试从这里突破 首先用扫描线来处理出以每个节点为右端点的区间的权值和,用可持久化线段树存下来 在所有的右端点相同的区间中,挑一个权值最大的 ...

  3. bzoj : 4504: K个串 区间修改主席树

    4504: K个串 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 268  Solved: 110[Submit][Status][Discuss] ...

  4. [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)

    [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...

  5. bzoj 4504: K个串【大根堆+主席树】

    像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么快速求区间和,用可持久化线段树维护(主席树?) ...

  6. SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)

    题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...

  7. BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)

    首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...

  8. BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)

    BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...

  9. bzoj 3514: GERALD07加强版 lct+可持久化线段树

    题目大意: N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. 题解: 这道题考试的时候没想出来 于是便爆炸了 结果今天下午拿出昨天准备的题表准备做题的时候 题表里就有这题 ...

随机推荐

  1. 九度OJ 1194:八进制 (进制转换)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:3521 解决:2058 题目描述: 输入一个整数,将其转换成八进制数输出. 输入: 输入包括一个整数N(0<=N<=100000 ...

  2. 【Robot Framework】---- Robot Framework简介、特点、RIDE

    Robot Framework简介.特点.RIDE 一.简介.特点. Robot Framework是一款python编写的功能自动化测试框架.具备良好的可扩展性,支持关键字驱动,可以同时测试多种类型 ...

  3. AbstractQueuedSynchronizer(一)

    应该将子类定义为非公共内部帮助器类,一般并发包类用内部类Sync sync来继承并实现.为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(信号量.事件,等等)提供一个框架.此类的设计目 ...

  4. Android SDK上手指南 3:用户交互

    在这篇教程中,我们将对之前所添加的Button元素进行设置以实现对用户点击的检测与响应.为了达成这一目标,我们需要在应用程序的主Activity类中略微涉及Java编程内容.如果大家在Java开发方面 ...

  5. JSP页面EL表达式不解析

    问题是这样:在搭建springMVC环境的时候,笔者写了一个简单的Controller如下: @Controller public class HelloController { @RequestMa ...

  6. winform窗体取消最大化双击标题最大化

    实现目标,固定窗体大小,1.窗体标题去掉最大化按钮2.双击窗体标题也不会最大化,彻底取消最大化 问题,如果设置窗体MaximizeBox和MinimumSize属性,看似问题解决了,单随之而来的问题是 ...

  7. Linux查看硬盘使用情况

    df df - report file system disk space usage df是查看文件系统磁盘使用情况的命令.如: # df -h Filesystem Size Used Avail ...

  8. Demo Nec

    /* 布局 */.g-va{width:1160px;margin:0 auto;} /* visual area */ /* 模块 */.m-nav{position:relative;height ...

  9. ansible普通用户su切换问题

    在现网应用中,安全加固后的主机是不允许直接以root用户登陆的,而很多命令又需要root用户来执行,在不改造现网的情况下.希望通过一个普通用户先登陆,再su切到root执行.而且每台主机的普通用户和r ...

  10. js 数组的所有操作

    js的数组操作有很多,这里记录了常用的和不常用的数组操作方法. 一.数组的创建 数组的创建有两种方法,一种是通过字面量,另一种是通过Array构造函数. 1.字面量 var num1 = [1,2,3 ...