• P3332 [ZJOI2013]K大数查询


    传送门

    注意操作 $1$ 是在区间的每个位置加入一个数,不是加上一个值

    相当于每个位置维护的是一个集合

    显然树套树

    一开始想的是区间线段树套权值线段树

    发现这样询问区间第 $K$ 大时就要先二分答案再用 $O(log^2_n)$ 时间查询

    那么单次询问的复杂度就有 $O(log^3_n)$ ,显然不行

    考虑权值线段树套区间线段树

    单次插入复杂度还是 $O(log^2_n)$,询问时只要在权值线段树上二分就行

    那么单次操作复杂度就是 $O(log^2_n)$

    据说此题很卡常,为了防止我的大常数导致 $GG$ 所以学了一下线段树的标记用久化

    简单说就是标记不下传,只要询问时把经过节点的标记加进来一起计算贡献,代码不难想

    因为空间问题所以内层的区间线段树要动态开点

    因为插入的数可能为负,所以要离散化,注意 $long long$ !

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    inline ll readll()
    {
        ll x=0; int f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e5+7,M=2e7+7;
    int n,m,cnt,A[N],T;
    int rt[N],L[M],R[M],tag[M];//区间线段树的数组,根节点,左儿子,右儿子,永久标记
    ll sum[M];//区间线段树数组,区间和
    int ql,qr; ll res;
    void add(int &o,int l,int r)//区间加1
    {
        if(!o) o=++cnt;//动态开点
        sum[o]+= max( min(r,qr)-max(l,ql)+1 ,0) ;//因为永久标记所以要这样更新sum
        if(l>=ql&&r<=qr) { tag[o]++; return; }//注意我的tag是给儿子的,要先更新sum
        int mid=l+r>>1;
        if(ql<=mid) add(L[o],l,mid);
        if(qr>mid) add(R[o],mid+1,r);
    }
    void query(int o,int l,int r,int tot)//区间求和,tot维护当前经过节点的tag的和
    {
        if(l>=ql&&r<=qr) { res+=sum[o]+1ll*(r-l+1)*tot; return; }//更新res
        int mid=l+r>>1;
        if(ql<=mid) query(L[o],l,mid,tot+tag[o]);
        if(qr>mid) query(R[o],mid+1,r,tot+tag[o]);
    }
    int POS,RES; ll K;
    void ADD(int o,int l,int r)//权值线段树插入
    {
        add(rt[o],1,n); if(l==r) return;
        int mid=l+r>>1;
        if(POS<=mid) ADD(o<<1,l,mid);
        else ADD(o<<1|1,mid+1,r);
    }
    void QUERY(int o,int l,int r)//权值线段树处理询问
    {
        if(l==r) { RES=A[l]; return; }
        int mid=l+r>>1;
        res=0; query(rt[o<<1|1],1,n,0);//注意优先走大的!
        if(res<K) { K-=res; QUERY(o<<1,l,mid); }
        else QUERY(o<<1|1,mid+1,r);
    }
    int op[N],dl[N],dr[N],dk[N];//读入的数据
    int main()
    {
        //freopen("data.in","r",stdin);
        //freopen("data.out","w",stdout);
        n=read(),m=read();
        for(int i=1;i<=m;i++)
        {
            op[i]=read(),dl[i]=read(),dr[i]=read(),dk[i]=readll();
            if(op[i]==1) A[++T]=dk[i];
        }
        sort(A+1,A+T+1);
        T=unique(A+1,A+T+1)-A-1;//离散化
        for(int i=1;i<=m;i++)
            if(op[i]==1) dk[i]=lower_bound(A+1,A+T+1,dk[i])-A;
        for(int i=1;i<=m;i++)
        {
            ql=dl[i],qr=dr[i];
            if(op[i]==1) { POS=dk[i]; ADD(1,1,T); }
            else
            {
                K=dk[i]; QUERY(1,1,T);
                printf("%d
    ",RES);
            }
        }
        return 0;
    }
  • 相关阅读:
    设计模式之六大设计原则学习笔记
    java多线程学习笔记
    mac上安装mongodb数据库教程
    在mac下使用终端命令通过ssh协议连接远程linux系统,代替windows的putty
    从request对象中获取请求json格式的参数
    @Conditional注释
    lambda 根据实体类的拼音排序
    PHP一行代码获取时间戳
    PHP导出生成Excel文件
    Mysql merge引擎介绍
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10648208.html
Copyright © 2020-2023  润新知