BZOJ2006: [NOI2010]超级钢琴
$n \leq 5e5$的正负数数列,求长度在$[L,R]$的区间和前$K \leq 5e5$大的区间和的和。
先把区间和变为两个前缀和相减。定一移二,确定区间右端点,然后左端点就是一个范围。用个堆把每个右端点能取得的区间和最大的那个丢进去,咋知道哪个最大?右端点前缀和一定,找左端点在一定范围内的最小:
方法一:如果打算用主席树,那就把三元组$(x,v,k)$丢进堆里,表示右端点为$x$,某个区间和为$v$,这个区间是$x$为右端点时对应范围的第$k$大的区间,每次取出时把$x$为右端点对应区间的第$k+1$大丢进堆里。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<queue> 6 //#include<time.h> 7 //#include<complex> 8 #include<algorithm> 9 #include<stdlib.h> 10 using namespace std; 11 12 int n,K,ll,rr; 13 #define maxn 500011 14 int sum[maxn],a[maxn]; 15 16 struct qnode{int id,v,k; bool operator < (const qnode &b) const {return v<b.v;}}; 17 priority_queue<qnode> q; 18 19 int root[maxn]; 20 struct SMT 21 { 22 struct Node 23 { 24 int ls,rs; 25 int cnt; 26 }a[maxn*20]; 27 int size,n; 28 void clear(int m) {n=m; size=0;} 29 void up(int x) {a[x].cnt=a[a[x].ls].cnt+a[a[x].rs].cnt;} 30 void insert(int &x,int y,int L,int R,int v) 31 { 32 x=++size; a[x].cnt=a[y].cnt+1; 33 if (L==R) {a[x].ls=a[x].rs=0; return;} 34 int mid=(L+R)>>1; 35 if (v<=mid) insert(a[x].ls,a[y].ls,L,mid,v),a[x].rs=a[y].rs; 36 else insert(a[x].rs,a[y].rs,mid+1,R,v),a[x].ls=a[y].ls; 37 } 38 void insert(int &x,int y,int v) {insert(x,y,1,n,v);} 39 int kth(int x,int y,int k) 40 { 41 if (k>a[y].cnt-a[x].cnt) return 0; 42 int L=1,R=n; 43 while (L<R) 44 { 45 int mid=(L+R)>>1; 46 if (a[a[y].ls].cnt-a[a[x].ls].cnt>=k) R=mid,x=a[x].ls,y=a[y].ls; 47 else k-=a[a[y].ls].cnt-a[a[x].ls].cnt,L=mid+1,x=a[x].rs,y=a[y].rs; 48 } 49 return L; 50 } 51 }t; 52 53 #define LL long long 54 int lisa[maxn],li=0; 55 int main() 56 { 57 scanf("%d%d%d%d",&n,&K,&ll,&rr); 58 lisa[++li]=sum[0]; for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i],lisa[++li]=sum[i]; 59 sort(lisa+1,lisa+1+li); li=unique(lisa+1,lisa+1+li)-lisa-1; 60 t.clear(li); t.insert(root[0],root[n+3],(sum[0]=lower_bound(lisa+1,lisa+1+li,sum[0])-lisa)); 61 for (int i=1;i<=n;i++) t.insert(root[i],root[i-1],(sum[i]=lower_bound(lisa+1,lisa+1+li,sum[i])-lisa)); 62 for (int i=ll;i<=n;i++) q.push((qnode){i,lisa[sum[i]]-lisa[t.kth(root[i-rr-1<0?n+3:i-rr-1],root[i-ll],1)],1}); 63 64 LL ans=0; 65 for (int i=1;i<=K;i++) 66 { 67 qnode now=q.top(); q.pop(); 68 ans+=now.v; 69 int tmp; 70 if ((tmp=t.kth(root[now.id-rr-1<0?n+3:now.id-rr-1],root[now.id-ll],now.k+1))>0) 71 q.push((qnode){now.id,lisa[sum[now.id]]-lisa[tmp],now.k+1}); 72 } 73 printf("%lld\n",ans); 74 return 0; 75 }
方法二:如果打算有st表,每次取掉堆中一个元素后,由于st表不支持删除,那就把那个区间以删除的点为界劈成两半再丢堆里,$(x,v,a,b,c)$表示右端点$x$,当前区间和$v$,是在$[a,b]$区间找到的左端点$c$。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<queue> 6 //#include<time.h> 7 //#include<complex> 8 #include<algorithm> 9 #include<stdlib.h> 10 using namespace std; 11 12 int n,K,ll,rr; 13 #define maxn 500011 14 int sum[maxn],a[maxn]; 15 16 struct qnode{int id,v,a,b,c; bool operator < (const qnode &b) const {return v<b.v;}}; 17 priority_queue<qnode> q; 18 19 int st[maxn][22],Log[maxn]; 20 int query(int x,int y) 21 { 22 int l=Log[y-x+1]; 23 return sum[st[x][l]]<sum[st[y-(1<<l)+1][l]]?st[x][l]:st[y-(1<<l)+1][l]; 24 } 25 26 #define LL long long 27 int main() 28 { 29 scanf("%d%d%d%d",&n,&K,&ll,&rr); 30 st[0][0]=0; 31 for (int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i],st[i][0]=i; 32 Log[0]=-1; for (int i=1;i<=n+1;i++) Log[i]=Log[i>>1]+1; 33 for (int j=1;j<=20;j++) 34 for (int i=0,to=(n-(1<<j)+1);i<=to;i++) 35 st[i][j]=sum[st[i][j-1]]<sum[st[i+(1<<(j-1))][j-1]]?st[i][j-1]:st[i+(1<<(j-1))][j-1]; 36 for (int i=ll;i<=n;i++) 37 { 38 int tmp=query(max(0,i-rr),i-ll); 39 q.push((qnode){i,sum[i]-sum[tmp],max(0,i-rr),i-ll,tmp}); 40 } 41 LL ans=0; 42 for (int i=1;i<=K;i++) 43 { 44 qnode now=q.top(); q.pop(); 45 ans+=now.v; 46 int tmp; 47 if (now.c>now.a) 48 { 49 tmp=query(now.a,now.c-1); 50 q.push((qnode){now.id,sum[now.id]-sum[tmp],now.a,now.c-1,tmp}); 51 } 52 if (now.c<now.b) 53 { 54 tmp=query(now.c+1,now.b); 55 q.push((qnode){now.id,sum[now.id]-sum[tmp],now.c+1,now.b,tmp}); 56 } 57 } 58 printf("%lld\n",ans); 59 return 0; 60 }