bzoj2006: [NOI2010]超级钢琴(主席树+优先队列)
bzoj2006: [NOI2010]超级钢琴
思路
对所有前缀和建权值线段树,先将所有右端点对应的最大的左端点丢进优先队列里,每次将优先队列队首的区间取出来后,将rk[r]++,从这个右端点对应的主席树区间中找到排名第rk[r]的值丢进优先队列,相当于用主席树求区间第k小。
代码
#include <bits/stdc++.h>
using namespace std;
#define dd(x) cout<<#x<<"="<<x<<" "
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define de(x) cout<<#x<<"="<<x<<"\n"
#define mes(p) memset(p,0,sizeof(p))
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define sz(x) (int)x.size()
#define pb push_back
#define ls (rt<<1)
#define rs (ls|1)
#define all(x) x.begin(),x.end()
const int maxn=500005;
typedef long long ll;
typedef vector <int > vi;
typedef pair <int, int> pi;
int n,k,L,R, cnt;
ll a[maxn], s[maxn];
int T[maxn], rk[maxn];
vector<ll > v;
struct tr{
int sum,l,r;
}t[maxn<<5];
struct node {
ll s;
int t;
node (){}
node(ll _s,int _t){
s=_s, t=_t;
}
bool operator < (const node& a) const{
return s<a.s;
}
};
priority_queue<node> q;
void update(int pre,int &now,int l,int r,int pos){
now = ++ cnt;
t[now].sum = t[pre].sum+1;
if(l==r) return ;
t[now].l=t[pre].l;
t[now].r=t[pre].r;
int mid = l+r>>1;
if(pos<=mid) update(t[pre].l,t[now].l,l,mid,pos);
else update(t[pre].r,t[now].r,mid+1,r,pos);
}
int qr(int x,int y,int l,int r,int k){
if(l==r) return l;
int mid = l+r>>1;
int sl = t[t[y].l].sum - t[t[x].l].sum;
if(sl>=k) return qr(t[x].l,t[y].l,l,mid,k);
else return qr(t[x].r,t[y].r,mid+1,r,k-sl);
}
int main(){
scanf("%d%d%d%d",&n,&k,&L,&R);
rep(i,1,n+1) cin>>a[i], s[i]=s[i-1]+a[i],v.pb(s[i]);
v.pb(0);
sort(all(v));
v.erase(unique(all(v)),v.end());
int m= sz(v);
update(T[0],T[1],1,m,lower_bound(all(v),0)-v.begin()+1);
rep(i,2,n+2){
update(T[i-1],T[i],1,m,lower_bound(all(v),s[i-1])-v.begin()+1);
int l=max(1,i-R), r=i-L;
if(r<=0) continue;
int ss = qr(T[l-1],T[r],1,m,1);
q.push(node(s[i-1]-v[ss-1],i));
rk[i-1]=1;
}
ll ans = 0;
while(k--){
ans += q.top().s;
int x=q.top().t;
q.pop();
int l=max(1,x-R), r=x-L, ss;
++rk[x-1];
if(rk[x-1]>t[T[r]].sum-t[T[l-1]].sum) continue;
ss = qr(T[l-1],T[r],1,m,rk[x-1]);
q.push(node(s[x-1]-v[ss-1],x));
}
printf("%lld",ans);
return 0;
}