bzoj3110 K大数查询(整体二分+线段树)

整体二分

①将修改和查询看做是一条队列。
②二分一个答案Mid,然后扫一下队列,用数据结构来依次(依照操作和询问顺序地)维护修改权值小于或等于Mid的修改对每个查询的贡献。
③将数据结构清空。(注意时间复杂度)
④将修改和询问分成两份,其中
询问:如果问题答案小于或等于Mid,就放在队列1。
如果问题答案大于Mid,修改询问(减掉小于或等于Mid的问题对本答案的贡献),然后放在队列2。
修改:如果修改权值大于或等于Mid,就放在队列1,否则放在队列2。
⑤递归往下做。

程序

#include <iostream>
#include <algorithm>
#include <string.h>
#include <string>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
using namespace std;

struct Query{
    int q,L,R,v,e;
}q[50100],p1[50100],p2[50100];
int T[201000],ad[201000],no[50100],ans[50100],n,m;

void down(int ro,int L,int R) {
    int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
    T[zuo]+=(mid-L+1)*ad[ro];ad[zuo]+=ad[ro];
    T[you]+=(R-mid)*ad[ro];ad[you]+=ad[ro];
    ad[ro]=0;
}

void update(int ro,int L,int R,int le,int ri) {
    if (le<=L&&R<=ri) {
        T[ro]+=R-L+1; ad[ro]++;
        return ;
    }
    if (L>ri||R<le) return;
    if (ad[ro]) down(ro,L,R);
    int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
    update(zuo,L,mid,le,ri);update(you,mid+1,R,le,ri);
    T[ro]=T[zuo]+T[you];
}

int query(int ro,int L,int R,int le,int ri) {
    if (le<=L&&R<=ri) return T[ro];
    if (L>ri||R<le) return 0;
    if (ad[ro]) down(ro,L,R);
    int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
    return query(zuo,L,mid,le,ri)+query(you,mid+1,R,le,ri);
}

void build(int ro,int L,int R) {
    if (T[ro]==0) return;
    if (L==R) {T[ro]=ad[ro]=0;return;}
    int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
    build(zuo,L,mid);build(you,mid+1,R);
    T[ro]=ad[ro]=0;
}

void solve(int he,int ta,int L,int R) {
    if (ta<he) return;
    if (L==R) {
        for (int i=he;i<=ta;i++) 
        if (q[i].q==2)
        ans[q[i].e]=L;
        return ;
    }
    int mid=L+R>>1;
    for (int i=he;i<=ta;i++) {
        if (q[i].q==1&&q[i].v<=mid)
            update(1,1,n,q[i].L,q[i].R);
        else if (q[i].q==2)
            no[i]=query(1,1,n,q[i].L,q[i].R);
    }
    build(1,1,n);
    int cnt1=0,cnt2=0;
    for (int i=he;i<=ta;i++)
        if (q[i].q==1) 
            if (q[i].v<=mid) p1[++cnt1]=q[i];
            else p2[++cnt2]=q[i];
        else if (no[i]>=q[i].v) p1[++cnt1]=q[i];
            else {q[i].v-=no[i];p2[++cnt2]=q[i];}

    for (int i=1;i<=cnt1;i++) q[i+he-1]=p1[i];
    for (int i=1;i<=cnt2;i++) q[he+cnt1+i-1]=p2[i];

    solve(he,he+cnt1-1,L,mid);
    solve(he+cnt1,ta,mid+1,R);
}

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++){
        int x,y,z,w;
        scanf("%d%d%d%d",&x,&y,&z,&w);
        if (x==1)w=-w;
        q[i] = (Query) {x,y,z,w,i};
    }
    for (int i=1;i<=m;i++) ans[i]=-60000;
    solve(1,m,-50100,50100);
    for (int i=1;i<=m;i++)
    if (ans[i]!=-60000) {
        if (ans[i]==1) printf("50000\n");
        else printf("%d\n",-ans[i]);
    }

    return 0;
}

推荐看

http://www.cnblogs.com/dirge/p/5810855.html

Jeanne d’Arc镇楼

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值