首先如果没有出现次数的限制的话,这题就是超级钢琴

但由于有了这个限制,不能简单地用前缀和

考虑顺着做的时候每个点的贡献,如果a[i]=x,x上次出现位置是lst[x](可以用一个map来记),那它会给右端点为[i,N],左端点为[lst[x]+1,i]的区间带来x的贡献

根据szr巨佬的说法,主席树的本质就是前缀和套线段树,所以我们如果按区间的右端点建主席树的根,每颗线段树内部存每个位置作为左端点的最大值,只需要给root[i]这棵树上[lst[x]+1,i]做区间+=x就可以了

而且i后面的位置的树还没有建出来,所以不用担心修改以后的更新问题

那么主席树上怎么区间修改呢...对于这道题,只需要像正常的线段树一样pushdown,update,但是每次修改子节点的时候都是新开一个点,然后把信息拷贝过去再修改,在连过去,防止改到前面线段树上的值

然后开优先队列,记下来(x,v,l,r,m)表示右端点为x,在[l,r]的范围内最大值是v,在m取到,每次取出来队顶,然后分割成(x,v',l,m-1,m')和(x,v",m+1,r,m")再塞回队列里,这样做K-1次,最后的队顶就是答案

时空复杂度大概都是$O(nlogn)$的,空间要开大一点。

 #include<bits/stdc++.h>
#define pa pair<ll,int>
#define CLR(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=1e5+,maxp=maxn*;
const ll inf=1e18; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} struct Node{
int x,l,r,m;ll v;
Node(ll a=,int b=,int c=,int d=,int e=){
v=a,x=b,l=c,r=d,m=e;
}
};
bool operator < (Node a,Node b){return a.v<b.v;} ll laz[maxp];int ch[maxp][],root[maxn],pct;
pa v[maxp];
int N,K;
map<int,int> lst;
priority_queue<Node> q; inline void update(int p){
v[p]=max(v[ch[p][]],v[ch[p][]]);
}
inline int add(int p,ll y){
v[++pct]=v[p];
ch[pct][]=ch[p][],ch[pct][]=ch[p][];
laz[pct]=laz[p]+y;
v[pct].first+=y;
return pct;
}
inline void pushdown(int p){
if(!laz[p]) return;
ch[p][]=add(ch[p][],laz[p]);
ch[p][]=add(ch[p][],laz[p]);
laz[p]=;
} void build(int &p,int l,int r){
p=++pct;
if(l==r) v[p]=make_pair(,l);
else{
int m=l+r>>;
build(ch[p][],l,m);
build(ch[p][],m+,r);
update(p);
}
} void insert(int &p,int pre,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){
p=add(pre,z);
}else{
pushdown(p);
int m=l+r>>;p=++pct;
ch[p][]=ch[pre][],ch[p][]=ch[pre][];
if(x<=m) insert(ch[p][],ch[pre][],l,m,x,y,z);
if(y>=m+) insert(ch[p][],ch[pre][],m+,r,x,y,z);
update(p);
}
} pa query(int p,int l,int r,int x,int y){
pushdown(p);
// printf("%d %d %d %d\n",l,r,x,y);
if(x<=l&&r<=y) return v[p];
else{
int m=l+r>>;pa re=make_pair(-inf,-);
if(x<=m) re=query(ch[p][],l,m,x,y);
if(y>=m+) re=max(re,query(ch[p][],m+,r,x,y));
return re;
}
} int main(){
//freopen("","r",stdin);
int i,j,k;
N=rd(),K=rd();
build(root[],,N);
for(i=;i<=N;i++){
int x=rd();
insert(root[i],root[i-],,N,lst[x]+,i,x);
// printf("mm");
lst[x]=i;
pa re=query(root[i],,N,,i);
// printf("%d %d %d\n",i,re.first,re.second);
q.push(Node(re.first,i,,i,re.second));
}
for(i=;i<K;i++){
Node p=q.top();q.pop();
pa rl,rr;
// pa rl=query(root[p.x],1,N,p.l,p.m-1);
// pa rr=query(root[p.x],1,N,p.m+1,p.r);
if(p.l<p.m) rl=query(root[p.x],,N,p.l,p.m-),q.push(Node(rl.first,p.x,p.l,p.m-,rl.second));
if(p.m<p.r) rr=query(root[p.x],,N,p.m+,p.r),q.push(Node(rr.first,p.x,p.m+,p.r,rr.second));
}
printf("%lld\n",q.top().v);
return ;
}

bzoj4504 K个串 (优先队列+主席树)的更多相关文章

  1. 【BZOJ4504&&Hihocoder1046】K个串(主席树,堆)

    题意:一个长度为n的数字序列,选出其中的一个连续子串,然后统计其子串中所有数字之和(注意这里重复出现的数字只被统计一次)询问第k大的和是多少 1 <= n <= 100000, 1 < ...

  2. bzoj4504 k个串 kstring 可持久化线段树 (标记永久化)

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

  3. luogu2048 [NOI2010]超级钢琴 (优先队列+主席树)

    思路:先扫一遍所有点作为右端点的情况,把它们能产生的最大值加到一个优先队列里,然后每次从优先队列里取出最大值,再把它对应的区间的次大值加到优先队列里,这样做K次 可以用一个前缀和,每次找i为右端点的第 ...

  4. PAT天梯赛练习题 L3-002. 堆栈(线段树查询第K大值或主席树)

    L3-002. 堆栈 时间限制 200 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 大家都知道“堆栈”是一种“先进后出”的线性结构,基本操作有 ...

  5. SPOJ 10628. Count on a tree (树上第k大,LCA+主席树)

    10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are number ...

  6. COGS 930. [河南省队2012] 找第k小的数 主席树

    主席树裸板子 #include<cstdio> #include<iostream> #include<algorithm> #define MAXN 100005 ...

  7. [xdoj1216]子树第k小(dfs序+主席树)

    解题关键:dfs序将树映射到区间,然后主席树求区间第k小,为模板题. #pragma comment(linker, "/STACK:1024000000,1024000000") ...

  8. HDU6621 K-th Closest Distance 第 k 小绝对值(主席树(统计范围的数有多少个)+ 二分 || 权值线段树+二分)

    题意:给一个数组,每次给 l ,r, p, k,问区间 [l, r] 的数与 p 作差的绝对值的第 k 小,这个绝对值是多少 分析:首先我们先分析单次查询怎么做: 题目给出的数据与多次查询已经在提示着 ...

  9. K Seq HihoCoder - 1046 || BZOJ4504 k个串

    这题与超级钢琴类似,然而重复的不重复计算贡献.. 那么先求出数组nxt,nxt[i]表示第i个元素之后的第一个与其相等的元素的下标,不存在则nxt[i]=0 考虑取的区间左端点为1时的情况. 将读入序 ...

随机推荐

  1. 理解标准盒模型和怪异模式&box-sizing属性

    盒子模型 主要有两种,w3c标准盒模型,IE下的怪异盒模型,其实还有就是弹性盒模型(上篇文章我们用他很好的解决了对齐问题) DTD规范 盒模型分为:标准w3c盒模型.IE盒模型.以及css中的伸缩盒模 ...

  2. WPF 录屏软件研发心得及思路分享(已结束开发)

    最近由于工程需要开始研发基于Windows的自动录屏软件,很多细节很多功能需要处理,毕竟一个完美的录屏软件不是你随随便便就可以写出来的.首先参考了大部分的录屏软件,在研发的过程中遇到了很多的问题:比如 ...

  3. LVM : 扩展文件系统的容量

    如果发现文件系统的容量不足了,可以通过 LVM 轻松的进行扩展(当然也可以进行缩减操作).本文将紧接前文中的 demo 详细的介绍扩展文件系统的操作过程.说明:本文的演示环境为 ubuntu 16.0 ...

  4. SpringBoot笔记--Jackson

    SpringUtil.getBean<GenericConversionService>().addConverter(Date2LocalDateTimeConverter()) var ...

  5. centos7.2部署vnc服务记录

    不做过多介绍了,下面直接记录下centos7系统下安装配置vncserver的操作记录 0)更改为启动桌面或命令行模式 获取当前系统启动模式 [root@localhost ~]# systemctl ...

  6. 软件工程启程篇章:结对编程和进阶四则运算(197 & 199)

    0x01 :序言:无关的事 I wrote a sign called "Dead End" in front of myself, but love crossed it wit ...

  7. Linux内核及分析 第八周 进程的切换和系统的一般执行过程

    学习笔记: 一.进程调度与进程调度的时机分析 1.不同类型的进程有不同需求的调度需求: 第一种分类: —I/O-bound:频繁的进行I/O,通常会花费很多时间等待I/O操作的完成 —CPU-boun ...

  8. Linux内核及分析 第五周 扒开系统调用的三层皮(下)

    实验内容: 1.执行rm menu -rf命令,强制删除原有的menu 2.使用git命令 git clone https://github.com/mengning/menu.git 克隆新的men ...

  9. Linux内核设计期中总结

    Linux内核设计期中总结 ● 知识点 一.计算机是如何工作的 计算机是按照冯·诺依曼存储程序的原理. 在执行程序时须先将要执行的相关程序和数据放入内存储器中,在执行程序时CPU根据当前程序指针寄存器 ...

  10. 20135327郭皓--Linux内核分析第八周 进程的切换和系统的一般执行过程

    第八周 进程的切换和系统的一般执行过程 一.进程切换的关键代码switch_to分析 1.进程调度与进程调度的时机分析 不同类型的进程有不同的调度需求 第一种分类: I/O-bound:频繁进行I/O ...