[ZJOI2013]K大数查询

[ZJOI2013]K大数查询

题目描述

有N个位置,M个操作。操作有两种,每次操作如果是:

  • 1 a b c:表示在第a个位置到第b个位置,每个位置加上一个数c
  • 2 a b c:表示询问从第a个位置到第b个位置,第C大的数是多少。

输入输出格式

输入格式:

 

第一行N,M接下来M行,每行形如1 a b c或2 a b c

 

输出格式:

 

输出每个询问的结果


solution

整体二分。

假设我们现在要解决[ql,qr] 并且他们的答案(加数操作就是值)在[l,r]

二分一个mid,我们维护一个区间。

我们把>=mid的操作对应区间都加1

对于询问,如果对应区间的值>=c,说明mid-r有最少c个了,那么它的答案就在[mid,r]里。

否则把值减去区间对应值,丢进[l,mid-1]里。

 

树状数组有个高级的区间加区间查写法,维护差分和差分*i

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 500005
using namespace std;
int n,m,Max,t1[maxn],t2[maxn];
struct node{
    int op,a,b,c,id,ans;
}q[maxn],s[maxn];
void add(int x,int v){
    for(int i=x;i<=n;i+=i&-i)t1[i]+=v,t2[i]+=x*v;
}
int ask(int x){
    int sum=0;
    for(int i=x;i;i-=i&-i){
        sum+=(x+1)*t1[i]-t2[i];
    }
    return sum;
}
void solve(int l,int r,int ql,int qr){
    if(l==r){
        for(int i=ql;i<=qr;i++)q[i].ans=l;
        return;
    }
    int mid=l+r+1>>1;
    int p=ql;
    for(int i=ql;i<=qr;i++){
        if(q[i].op==1){
            if(q[i].c>=mid){
                add(q[i].a,1);add(q[i].b+1,-1);
            }
            else p++;
        }
        else {
            q[i].ans=ask(q[i].b)-ask(q[i].a-1);
            if(q[i].ans<q[i].c)p++;
        }
    }
    int n1=ql,n2=p;
    for(int i=ql;i<=qr;i++){
        if(q[i].op==1){
            if(q[i].c>=mid){
                add(q[i].a,-1);add(q[i].b+1,1);
                s[n2++]=q[i];
            }
            else s[n1++]=q[i];
        }
        else {
            if(q[i].ans<q[i].c){
                q[i].c-=q[i].ans;
                s[n1++]=q[i];
            }
            else s[n2++]=q[i];
        }
    }
    for(int i=ql;i<=qr;i++)q[i]=s[i];
    solve(l,mid-1,ql,p-1);solve(mid,r,p,qr);
}
bool ID(node a,node b){return a.id<b.id;}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&q[i].op,&q[i].a,&q[i].b,&q[i].c);
        Max=max(Max,q[i].c);
        q[i].id=i;
    }
    solve(1,Max,1,m);
    sort(q+1,q+m+1,ID);
    for(int i=1;i<=m;i++){
        if(q[i].op==2){
            printf("%d\n",q[i].ans);
        }
    }
    return 0;
}
View Code

 

posted @ 2019-02-21 09:24  liankewei123456  阅读(290)  评论(0编辑  收藏  举报