[NOI2010][bzoj2006] 超级钢琴 [主席树/ST表+堆]

Posted Orion545

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2010][bzoj2006] 超级钢琴 [主席树/ST表+堆]相关的知识,希望对你有一定的参考价值。

题面:

传送门

思路:

首先容易想到用堆维护的O(n2logn)暴力

那么肯定就是在这个基础上套数据结构了【愉快】

然而我因为过于蒟蒻......只想得到主席树暴力***过去的方法

大概就是把前缀和算出来,然后放到一棵线段树里面

对于每一个i(i=1...(n-L+1)),线段树查询以i为左端点的所有区间右端点中,前缀和最大的一个

因为区间[i,j]可以变成pre[j]-pre[i]的形式,那么只需要最大化pre[j]就可以了

一开始,对于所有的i把这个最大值求出来,减掉pre[i-1],再放到堆里面

每次从堆顶取出当前最大值,然后答案加上这个值,同时主席树上修改一波,把这个点在树上的值改成-inf

其实一共有n棵主席树,只不过每一棵都公用同一个初始树,后面每次修改又只会新开logn的节点,所以总空间复杂度应该是O(nlogn+klogn)的

就这样,总时间复杂度O(nlogn+klogn),常数略大

然而,菊苣们早已开发出了新的希望算法,那就是查询O(1)的ST表

把上文中的初始询问(n个,在初始线段树上的那些)转化成三元组(i,L,R),代表左端点是i的,长度在[L,R]之内的序列最大值

那么,每次堆顶弹出以后,可以这样操作:

设堆顶元素为x,我们新建两个三元组(i,L,x-1)(i,x+1,R),把这两个三元组的值求出来放到堆里面去即可

效率虽然还是O(nlogn+klogn),但是常数比我的算法不知道高到哪里去了,跑的贼快

Code:

不知道为什么,500000的点需要开两千五百万的数组......连续3次RE

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<queue>
 6 #define inf 1e15
 7 #define ll long long
 8 using namespace std;
 9 inline int read(){
10     int re=0,flag=1;char ch=getchar();
11     while(ch>9||ch<0){
12         if(ch==-) flag=-1;
13         ch=getchar();
14     }
15     while(ch>=0&&ch<=9) re=(re<<1)+(re<<3)+ch-0,ch=getchar();
16     return re*flag;
17 }
18 struct node{
19     ll w;int pos;
20     bool operator <(const node &b){
21         return w<b.w;
22     }
23 }a[25000010];
24 struct NODE{
25     ll w;int pos1,pos2;
26     bool operator <(const NODE &b)const{
27         return w<b.w;
28     }
29 };
30 int n,m,L,R,x[500010];
31 int ch[25000010][2],cnt,root[500010];ll pre[500010];
32 priority_queue<NODE>q;
33 void update(int x){
34     if(a[ch[x][0]].w>=a[ch[x][1]].w) a[x].w=a[ch[x][0]].w,a[x].pos=a[ch[x][0]].pos;
35     else a[x].w=a[ch[x][1]].w,a[x].pos=a[ch[x][1]].pos;
36 }
37 int build(int l,int r){
38     int mid=(l+r)>>1,cur=++cnt;
39     if(l==r){
40         a[cur].w=pre[l];a[cur].pos=l;return cur;
41     }
42     ch[cur][0]=build(l,mid);ch[cur][1]=build(mid+1,r);
43     update(cur);
44     return cur;
45 }
46 node max(node l,node r){
47     return l<r?r:l;
48 }
49 node query(int l,int r,int ql,int qr,int cur){
50     int mid=(l+r)>>1;node re=(node){-inf,0};
51     if(l>=ql&&r<=qr) return a[cur];
52     if(mid>=ql) re=max(re,query(l,mid,ql,qr,ch[cur][0]));
53     if(mid<qr) re=max(re,query(mid+1,r,ql,qr,ch[cur][1]));
54     return re;
55 }
56 int change(int l,int r,int u,int his){
57     int mid=(l+r)>>1,cur=++cnt;
58     if(l==r){
59         a[cur]=(node){-inf,l};return cur;
60     }
61     if(u<=mid) ch[cur][1]=ch[his][1],ch[cur][0]=change(l,mid,u,ch[his][0]);
62     else ch[cur][0]=ch[his][0],ch[cur][1]=change(mid+1,r,u,ch[his][1]);
63     update(cur);return cur;
64 }
65 int main(){
66     freopen("piano.in","r",stdin);
67     freopen("piano.out","w",stdout);
68     int i,t1;ll ans=0;node tmp;NODE tt;
69     n=read();m=read();L=read();R=read();
70     for(i=1;i<=n;i++) x[i]=read(),pre[i]=pre[i-1]+(ll)x[i];
71     t1=build(1,n);
72     for(i=1;i<=n-L+1;i++){
73         root[i]=t1;tmp=query(1,n,min(n,i+L-1),min(n,i+R-1),t1);
74         q.push((NODE){-pre[i-1]+tmp.w,i,tmp.pos});
75     }
76     for(i=1;i<=m;i++){
77         ans+=q.top().w;tt=q.top();q.pop();
78         root[tt.pos1]=change(1,n,tt.pos2,root[tt.pos1]);
79         tmp=query(1,n,min(n,tt.pos1+L-1),min(n,tt.pos1+R-1),root[tt.pos1]);
80         q.push((NODE){-pre[tt.pos1-1]+tmp.w,tt.pos1,tmp.pos});
81     }
82     printf("%lld\n",ans);
83 }

 

以上是关于[NOI2010][bzoj2006] 超级钢琴 [主席树/ST表+堆]的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 2006: [NOI2010]超级钢琴

[BZOJ2006][NOI2010]超级钢琴

bzoj 2006 [NOI2010]超级钢琴 rmq+堆

BZOJ2006[NOI2010]超级钢琴 ST表+堆

[BZOJ2006][NOI2010]超级钢琴(ST表+堆)

[BZOJ2006] [NOI2010]超级钢琴 主席树+贪心+优先队列