P3332 [ZJOI2013]K大数查询

前言

谁知道是CDQ分治还是整体二分呢?
首先,如果只有一个查询,我们可以在区间里二分答案。
但是对于修改和操作动态搞,二分可办不了。
树套树?
不想写这么麻烦的数据结构。
那就上我们的CDQ/整体二分吧。
把操作和结果等一起二分

实现

整体过程:当遇到的操作是询问操作时,查询线段树里的当前区间,并将当前区间所包含的数的个数as与查询的第k大相比较,如果小于k,那么把当前询问分到左边去,并统计影响k-=as,反之,将它分到右边;当遇到的操作是加入操作时,把要加入的数和mid比较,如果大于mid,那么就把当前操作统计进去,而且当前操作会对右边产生影响,所以要分到右边去,虽然对左边也有影响,但在处理询问时已经处理,就不用考虑了。

统计:线段树维护,记录每个区间当前有多少个大于mid的数,“加入”操作里的“统计进去”即是更新当前区间的数的个数。(然而我们也可以用树状数组维护)

每次操作完,都要以区间序号为关键字重新排序。

注意,对于每次使用线段树,我们不能直接强行将整颗线段树memset,而是打删除的lazy标记,注意删除lazy标记与添加lazy标记的操作先后顺序。

最后:开long long

链接

BZOJ
COGS
Luogu

复杂度

O(f(len)logn)
然而加上线段树。
这里写图片描述
线段树递归常数大没办法,窝懒得写zkw,至于树状数组,我菜的连个区间修改都不会。
拉倒啦。。。。
有写zhw的神犇麻烦告诉窝一下速度。

AC Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#define il inline
#define ll long long
using namespace std;
const ll maxm=300010;
ll n,m;
struct node{
    ll opt,l,r,k,sum,num;
}a[maxm];
ll tree[maxm],del[maxm],adx[maxm];
ll ans[maxm];
il bool comp(node x1,node x2){return x1.sum<x2.sum;}
il void update(ll o){tree[o]=tree[(o<<1)]+tree[(o<<1)|1];}
il void pushdown(ll o,ll l,ll r)
{
    if(del[o]&&l!=r)
    {
        tree[(o<<1)]=tree[(o<<1)|1]=0;
        del[(o<<1)]=del[(o<<1)|1]=1;
        adx[(o<<1)]=adx[(o<<1)|1]=0;
        del[o]=0;
    }
    ll mid=(l+r)>>1;
    if(adx[o]!=0&&l!=r)  
     {  
        tree[(o<<1)]+=(mid-l+1)*adx[o]; tree[(o<<1)|1]+=(r-mid)*adx[o];  
        adx[(o<<1)]+=adx[o]; adx[(o<<1)|1]+=adx[o];  
        adx[o]=0;  
     }  
}
void modify(ll o,ll l,ll r,ll ql,ll qr,ll val)
{
    pushdown(o,l,r);
    if(ql<=l&&r<=qr)  
     {  
        tree[o]+=(r-l+1)*val;  
        adx[o]+=val;  
        return;  
     }  
    ll mid=(l+r)>>1;  
    if(ql<=mid) modify((o<<1),l,mid,ql,qr,val);  
    if(qr>mid) modify((o<<1)|1,mid+1,r,ql,qr,val);  
    update(o);  
}
ll ask(ll o,ll l,ll r,ll ql,ll qr)
{
    pushdown(o,l,r);
    if(ql<=l&&qr>=r) return tree[o];
    ll mid=(l+r)>>1;
    ll anx=0;
    if(ql<=mid) anx+=ask((o<<1),l,mid,ql,qr);
    if(qr>mid) anx+=ask((o<<1)|1,mid+1,r,ql,qr);
    return anx;
}
void slove(ll l,ll r,ll ql,ll qr)
{
    if(l==r)
    {
        for(ll i=ql;i<=qr;i++) 
        if(a[i].opt==2) ans[a[i].num]=l;
        return;
    }
    ll mid=(l+r)>>1,xl=0,xr=qr-ql+1;
    tree[1]=adx[1]=0,del[1]=1;
    for(ll i=ql;i<=qr;i++)
    {
        if(a[i].opt==1)
        {
            if(a[i].k<=mid) a[i].sum=++xl;
            else 
            {
                modify(1,1,n,a[i].l,a[i].r,1);
                a[i].sum=++xr;
            }
        }
        else
        {
            ll ax=ask(1,1,n,a[i].l,a[i].r);
            if(ax<a[i].k) a[i].sum=++xl,a[i].k-=ax;
            else a[i].sum=++xr;
        }
    }
    sort(a+ql,a+qr+1,comp);
    slove(l,mid,ql,ql+xl-1);
    slove(mid+1,r,ql+xl,qr);
}
il ll read()
{
    ll x=0,w=1;
    char ch=0;
    while(ch<'0'||ch>'9') 
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
     x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*w;
}
int main()
{
    //freopen("zjoi13_sequence.in","r",stdin);
    //freopen("zjoi13_sequence.out","w",stdout);
    n=read(),m=read();
    for(ll i=1;i<=m;i++)
     a[i].opt=read(),a[i].l=read(),a[i].r=read(),a[i].k=read(),a[i].num=i;
    slove(0,n,1,m);
    for(ll i=1;i<=m;i++)
     if(ans[i]) printf("%lld\n",ans[i]);
    return 0; 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值