cf1181D Irrigation[思维+权值线段树求第k小]

传送门

题意:给一个长为n的数组,q次询问(n,q<=5e5),每次给一个k(k<=1e18),求第k次填数是填第几个数(每次填数选择最小的数进行+1操作,如果有多个优先加下标最小的那个)

题解:离线,又由于每次改变都是成块地改变,所以可以成块改变就成块改变,不能成块改变就直接求第k小,可以用权值线段树求第k小

#include

using namespace std;
typedef long long ll;
#define debug(x) cout<<#x<<" is "<

const int maxn=5e5+5;
const double eps=1e-3;
const ll inf=1e18;

struct pot{
    int id;
    ll cnt;
}p[maxn],que[maxn];

ll a[maxn],b[maxn];
int c[maxn],ans[maxn];

bool cmp1(struct pot aa,struct pot bb){
    if(aa.cnt!=bb.cnt)return aa.cnt<bb.cnt;
    return aa.id<bb.id;
}

bool cmp2(struct pot aa,struct pot bb){
    if(aa.cnt!=bb.cnt)return aa.cnt<bb.cnt;
    return aa.id<bb.id;
}

struct node{
    int l;
    int r;
    int val;
}no[maxn<<2];

void build(int rt,int l,int r){
    no[rt].l=l;
    no[rt].r=r;
    no[rt].val=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build((rt<<1),l,mid);
    build((rt<<1)|1,mid+1,r);
}

void pushup(int rt){
    no[rt].val=no[rt<<1].val+no[(rt<<1)|1].val;
}

void update(int rt,int l,int r,int pos){
    if(l==r){
        no[rt].val=1;
        return;
    }
    int mid=(l+r)>>1;
    if(mid>=pos)update((rt<<1),l,mid,pos);
    else update((rt<<1)|1,mid+1,r,pos);
    pushup(rt);
}

int query(int rt,int l,int r,int pos){
    if(l==r)return l;
    int mid=(l+r)>>1;
    if(no[rt<<1].val>=pos)return query(rt<<1,l,mid,pos);
    else return query((rt<<1)|1,mid+1,r,pos-no[rt<<1].val);
}

int main(){
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);

    for(int i=1;i<=m;i++){
        p[i].id=i;
        p[i].cnt=0;
    }
    for(int i=1;i<=n;i++){
        int w;
        scanf("%d",&w);
        p[w].cnt++;
    }
    for(int i=1;i<=q;i++){
        scanf("%lld",&que[i].cnt);
        que[i].cnt-=n;
        que[i].id=i;
    }

    sort(p+1,p+1+m,cmp1);
    sort(que+1,que+1+q,cmp2);

    build(1,1,m);
    int st=0;
    ll sum=0;
    ll w=0;
    for(int i=1;i<=q;i++){
        ll k=que[i].cnt-w;
        while(st<m&&p[st+1].cnt*st-sum<k){
            k-=(p[st+1].cnt*st-sum);
            w+=(p[st+1].cnt*st-sum);
            sum=p[st+1].cnt*(st+1);
            st++;
            update(1,1,m,p[st].id);
        }
        k%=(st);
        if(!k)k=st;
        int w=query(1,1,m,k);
        ans[que[i].id]=w;
    }
    for(int i=1;i<=q;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

你可能感兴趣的:(权值线段树)