bzoj 4504: K个串 可持久化线段树+堆
题目:
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个串 可持久化线段树+堆的更多相关文章
- 【BZOJ4504】K个串 可持久化线段树+堆
[BZOJ4504]K个串 Description 兔子们在玩k个串的游戏.首先,它们拿出了一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计 ...
- hihocoder#1046 K个串 可持久化线段树 + 堆
首先考虑二分,然后发现不可行.... 注意到\(k\)十分小,尝试从这里突破 首先用扫描线来处理出以每个节点为右端点的区间的权值和,用可持久化线段树存下来 在所有的右端点相同的区间中,挑一个权值最大的 ...
- bzoj : 4504: K个串 区间修改主席树
4504: K个串 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 268 Solved: 110[Submit][Status][Discuss] ...
- [BZOJ 3123] [SDOI 2013]森林(可持久化线段树+并查集+启发式合并)
[BZOJ 3123] [SDOI 2013]森林(可持久化线段树+启发式合并) 题面 给出一个n个节点m条边的森林,每个节点都有一个权值.有两种操作: Q x y k查询点x到点y路径上所有的权值中 ...
- bzoj 4504: K个串【大根堆+主席树】
像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么快速求区间和,用可持久化线段树维护(主席树?) ...
- SPOJ-COT-Count on a tree(树上路径第K小,可持久化线段树)
题意: 求树上A,B两点路径上第K小的数 分析: 同样是可持久化线段树,只是这一次我们用它来维护树上的信息. 我们之前已经知道,可持久化线段树实际上是维护的一个前缀和,而前缀和不一定要出现在一个线性表 ...
- BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)
首先嘛,还是太弱了,想了好久QAQ 然后,这道题么,明显就是求sigma(size[x]) (x是y的儿子且层树小于k) 然后就可以发现:把前n个节点按深度建可持久化线段树,就能用前缀和维护了 其实不 ...
- BZOJ.2653.[国家集训队]middle(可持久化线段树 二分)
BZOJ 洛谷 求中位数除了\(sort\)还有什么方法?二分一个数\(x\),把\(<x\)的数全设成\(-1\),\(\geq x\)的数设成\(1\),判断序列和是否非负. 对于询问\(( ...
- bzoj 3514: GERALD07加强版 lct+可持久化线段树
题目大意: N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数. 题解: 这道题考试的时候没想出来 于是便爆炸了 结果今天下午拿出昨天准备的题表准备做题的时候 题表里就有这题 ...
随机推荐
- 解决 ie 返回json提示下载 ResponseEntity方法
js 配合java springMVC后台,成功后返回消息,chrom ff都正常,只有IE提交后返回的JSON提示下载,查看类型 application/json google后发现原来是IE不 ...
- ibatis中井号跟美元符号区别(#.$)
1.#可以进行预编译,进行类型匹配,#变量名# 会转化为 jdbc 的 类型 $不进行数据类型匹配,$变量名$就直接把 $name$替换为 name的内容 例如: select * from tabl ...
- 让Editplus支持sql语法高亮显示
版权声明:作者:jiankunking 出处:http://blog.csdn.net/jiankunking 本文版权归作者和CSDN共同拥有,欢迎转载.但未经作者允许必须保留此段声明,且在文章页面 ...
- js与jquey的表达
jquery 1.$("#id").css("display") 2.$(this) 3.attr(a,b) :在a里面追加元素b 4.prop: functi ...
- 如何在JSTL中获取数组或者list对象的索引值(index)
<c:forEach items="${productList}" var="products" varStatus="status" ...
- spring启动quartz定时器
在很多中经常要用到定时任务,quartz是定时器中比较好用的,在Spring中使用quartz是很容易的事情,首先在spring的applicationContext.xml文件中增加如下配置: &l ...
- (转)Spring 缓存EhCacheFactoryBean
Spring使用Cache 从3.1开始,Spring引入了对Cache的支持.其使用方法和原理都类似于Spring对事务管理的支持.Spring Cache是作用在方法上的,其核心思想是这样的:当我 ...
- PHP保存数组到文件中的方法
ThinkPHP自3.1以后的版本,F函数保存数组时先序列化后再保存到文件中,因为我需要使用C方法来读取自定义配置文件,故需要把PHP数组保存到文件中以便C方法读取,PHP保存数组到文件的方法如下: ...
- Data Structure Linked List: Detect and Remove Loop in a Linked List
http://www.geeksforgeeks.org/detect-and-remove-loop-in-a-linked-list/ #include <iostream> #inc ...
- [转]Mysql命令
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...