• bzoj3110 [Zjoi2013]K大数查询——线段树套线段树


    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3110

    外层权值线段树套内层区间线段树;

    之所以外层权值内层区间,是因为区间线段树需要标记下传,所以写在内层比较方便;

    然而空间太大了,所以动态开点,大约每个外层线段树的点上有 logn 个内层线段树点;

    最开始写的不知为何很快就WA:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const maxn=5e4+5,maxm=maxn*400;
    int n,m,rt[maxn<<2],ls[maxm],rs[maxm],cnt;
    int a[maxn],b[maxn],tp[maxn],c[maxn],tmp[maxn],tot;
    ll sum[maxm],lzy[maxm];//ll lzy 防止计算时爆int 
    void pushdown(int x,int l,int r)
    {
        if(!lzy[x])return;
        if(!ls[x])ls[x]=++cnt;
        if(!rs[x])rs[x]=++cnt;
        int mid=((l+r)>>1);
        sum[ls[x]]+=(mid-l+1)*lzy[x]; sum[rs[x]]+=(r-mid)*lzy[x];
        lzy[ls[x]]+=lzy[x]; lzy[rs[x]]+=lzy[x];
        lzy[x]=0;
    }
    void add(int &x,int l,int r,int L,int R)
    {
        if(!x)x=++cnt;
        if(l>=L&&r<=R){sum[x]+=(r-l+1); lzy[x]++; return;}
    //  pushdown(x,l,r);
        int mid=((l+r)>>1);
        if(mid>=L)add(ls[x],l,mid,L,R);
        if(mid<R)add(rs[x],mid+1,r,L,R);
        sum[x]=sum[ls[x]]+sum[rs[x]];
    }
    void insert(int x,int l,int r,int tl,int tr,int c)
    {
        add(rt[x],1,n,tl,tr);
        if(l==r)return;//
        int mid=((l+r)>>1);//权值区间 
        if(c<=mid)insert(x<<1,l,mid,tl,tr,c);
        else insert(x<<1|1,mid+1,r,tl,tr,c);
    }
    ll ask(int x,int l,int r,int L,int R)
    {
        if(!x)return 0;//
        if(l>=L&&r<=R)return sum[x];
        pushdown(x,l,r);
        int mid=((l+r)>>1); ll ret=0;
    //  if(mid>=L)ret+=ask(x<<1,l,mid,L,R);
    //  if(mid<R)ret+=ask(x<<1|1,mid+1,r,L,R);
        if(mid>=L)ret+=ask(ls[x],l,mid,L,R);
        if(mid<R)ret+=ask(rs[x],mid+1,r,L,R);//别写串了! 
        return ret;
    }
    int query(int x,int l,int r,int L,int R,int k)
    {
        if(l==r)return l;
        ll tmp=ask(rt[x<<1|1],1,n,L,R),mid=((l+r)>>1);//查询第k大 
        if(tmp>=k)return query(x<<1|1,mid+1,r,L,R,k);//
        else return query(x<<1,l,mid,L,R,k-tmp);//
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&tp[i],&a[i],&b[i],&c[i]);
            if(tp[i]==1)tmp[++tot]=c[i];
        }
        sort(tmp+1,tmp+n+1); tot=unique(tmp+1,tmp+n+1)-tmp-1;
        for(int i=1;i<=m;i++)
        {
            if(tp[i]==1)
            {
                int tt=lower_bound(tmp+1,tmp+tot+1,c[i])-tmp;
                insert(1,1,tot,a[i],b[i],tt);
            }
            else printf("%d
    ",tmp[query(1,1,tot,a[i],b[i],c[i])]);
        }
        return 0;
    }

    不会改了,所以模仿别人写了个标记永久化,然后WA惨;

    因为不太熟悉标记永久化,没注意到更新以及查询时要注意不能重复,改了半天,终于好了...

    学到了点标记永久化的细节。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int const maxn=5e4+5,maxm=maxn*400;
    int n,m,rt[maxn<<2],ls[maxm],rs[maxm],cnt;
    int a[maxn],b[maxn],tp[maxn],c[maxn],tmp[maxn],tot,L,R;
    ll sum[maxm],lzy[maxm];//ll lzy 防止计算时爆int 
    //void pushdown(int x,int l,int r)
    //{
    //    if(!lzy[x])return;
    //    if(!ls[x])ls[x]=++cnt;
    //    if(!rs[x])rs[x]=++cnt;
    //    int mid=((l+r)>>1);
    //    sum[ls[x]]+=(mid-l+1)*lzy[x]; sum[rs[x]]+=(r-mid)*lzy[x];
    //    lzy[ls[x]]+=lzy[x]; lzy[rs[x]]+=lzy[x];
    //    lzy[x]=0;
    //}
    void add(int &x,int l,int r,int L,int R)
    {
        if(!x)x=++cnt;
        if(l==L&&r==R){/*sum[x]+=(r-l+1);*/ lzy[x]++; return;}//==
        sum[x]+=(R-L+1);
    //    pushdown(x,l,r);
        int mid=((l+r)>>1);
    //    if(mid>=L)add(ls[x],l,mid,L,R);//会重复计算sum!(因为标记永久化) 
    //    if(mid<R)add(rs[x],mid+1,r,L,R);
    //    sum[x]=sum[ls[x]]+sum[rs[x]];
        if(mid<L)add(rs[x],mid+1,r,L,R);
        else if(mid>=R)add(ls[x],l,mid,L,R);
        else add(ls[x],l,mid,L,mid),add(rs[x],mid+1,r,mid+1,R);
    }
    ll ask(int x,int l,int r,int L,int R)
    {
        if(!x)return 0;//
        ll ret=lzy[x]*(R-L+1);
        if(l==L&&r==R)return ret+sum[x];//
    //    pushdown(x,l,r);
        int mid=((l+r)>>1); 
    //    if(mid>=L)ret+=ask(x<<1,l,mid,L,R);
    //    if(mid<R)ret+=ask(x<<1|1,mid+1,r,L,R);//别把ls,rs和x<<1,x<<1|1写串了!
    //    if(mid>=L)ret+=ask(ls[x],l,mid);
    //    if(mid<R)ret+=ask(rs[x],mid+1,r); //会重复计算!! 
    //    printf("ret=%lld
    ",ret);
        if(mid<L)return ret+ask(rs[x],mid+1,r,L,R);
        else if(mid>=R)return ret+ask(ls[x],l,mid,L,R);
        else return ret+ask(ls[x],l,mid,L,mid)+ask(rs[x],mid+1,r,mid+1,R);
    }
    int query(int x,int l,int r,int k)
    {
        if(l==r)return l;
        ll tmp=ask(rt[x<<1|1],1,n,L,R),mid=((l+r)>>1);//查询第k大 
        if(tmp>=k)return query(x<<1|1,mid+1,r,k);//
        else return query(x<<1,l,mid,k-tmp);//
    }
    void insert(int x,int l,int r,int c)
    {
        add(rt[x],1,n,L,R);
        if(l==r)return;//
        int mid=((l+r)>>1);//权值区间 
        if(c<=mid)insert(x<<1,l,mid,c);
        else insert(x<<1|1,mid+1,r,c);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d%d",&tp[i],&a[i],&b[i],&c[i]);
            if(tp[i]==1)tmp[++tot]=c[i];
        }
        sort(tmp+1,tmp+n+1); tot=unique(tmp+1,tmp+n+1)-tmp-1;
        for(int i=1;i<=m;i++)
        {
            L=a[i],R=b[i];
            if(tp[i]==1)
            {
                int tt=lower_bound(tmp+1,tmp+tot+1,c[i])-tmp;
                insert(1,1,tot,tt);
            }
            else printf("%d
    ",tmp[query(1,1,tot,c[i])]);
        }
        return 0;
    }
  • 相关阅读:
    表的锁机制
    ABAP函数:VIEW_MAINTENANCE_CALL(维护表视图等)
    数据字典中新建表时出现黄色警告的处理
    smartforms长文本处理方式
    打印报表页码
    可以根据柜子内表取出所有的柜子信息的BAPI函数
    方法的重载、类的继承
    构造方法
    函数方法
    方法
  • 原文地址:https://www.cnblogs.com/Zinn/p/9332324.html
Copyright © 2020-2023  润新知