bzoj2006: [NOI2010]超级钢琴(主席树+优先队列)

bzoj2006: [NOI2010]超级钢琴

bzoj2006: 超级钢琴

思路

对所有前缀和建权值线段树,先将所有右端点对应的最大的左端点丢进优先队列里,每次将优先队列队首的区间取出来后,将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;
}
posted @ 2019-05-09 01:01  Seast  阅读(117)  评论(0编辑  收藏  举报