• 洛谷 P3960 [ NOIP 2017 ] 列队 —— 线段树


    题目:https://www.luogu.org/problemnew/show/P3960

    NOIP 题,不用很复杂的数据结构...但又参考了许多;

    要求支持维护删除第 k 个和在末尾插入的数据结构,线段树就很好;

    所以每行一个线段树维护前 m-1 个元素,最后一列一个线段树即可;

    但 n+1 个线段树显然空间太大,考虑利用一开始是按顺序排列的特点;

    也就是不用实际开出来所有线段树的点,只要它是满的就可以直接算出来;

    对于新加入线段树的点,如果真的加入线段树里,又不会算空间了...

    但是新加入的点只有 2*q 个,可以给每行和最后一列开一个 vector 记录新加入的点,查询时如果不是原装点,就去 vector 里提取;

    也不需要去 vector 里删除元素,在对应线段树上动态开出这个点的位置表示一下就行了,将来查询的时候就不会算这个元素;

    把基础数据结构组合起来,得到简洁的做法...希望下次能自己想出来。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define mid ((l+r)>>1)
    using namespace std;
    typedef long long ll;
    int const maxn=3e5+5,maxm=12000000;
    int n,m,q,rt[maxn],cnt,sum[maxm],ls[maxm],rs[maxm],mx;
    vector<ll>v[maxn];//ll
    int rd()
    {
        int ret=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
        while(ch>='0'&&ch<='9')ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar();
        return ret*f;
    }
    int query(int x,int l,int r,int k)
    {
        if(l==r)return l; int num=(mid-l+1)-sum[ls[x]];
        if(num>=k)return query(ls[x],l,mid,k);
        return query(rs[x],mid+1,r,k-num);
    }
    void update(int &x,int l,int r,int pos)
    {
        if(!x)x=++cnt; sum[x]++;//sum 是删除的数量 
        if(l==r)return;//
        if(pos<=mid)update(ls[x],l,mid,pos);
        else update(rs[x],mid+1,r,pos);
    }
    ll del(int x,int y)
    {
        int pos=query(rt[x],1,mx,y);
        update(rt[x],1,mx,pos);
        if(pos<m)return (ll)(x-1)*m+pos;
        return v[x][pos-m];
    }
    ll del2(int x)
    {
        int pos=query(rt[n+1],1,mx,x);
        update(rt[n+1],1,mx,pos);
        if(pos<=n)return (ll)pos*m;
        return v[n+1][pos-n-1];
    }
    int main()
    {
        n=rd(); m=rd(); q=rd();
        mx=max(n,m)+q;
        for(int i=1,x,y;i<=q;i++)
        {
            x=rd(); y=rd();
            if(y==m)
            {
                ll tmp=del2(x);
                printf("%lld
    ",tmp);
                v[n+1].push_back(tmp);
            }
            else
            {
                ll tmp=del(x,y);
                printf("%lld
    ",tmp);
                v[n+1].push_back(tmp);
                ll tmp2=del2(x);
                v[x].push_back(tmp2);
            }
        }
        return 0;
    }
  • 相关阅读:
    文件与流之动手动脑
    四则运算1.1版
    2018年11月16日编程体会
    JAVA(1)之关于对象数组作形参名的方法的使用
    re模块 时间模块
    日志模块
    软件开发目录规范
    模块和包
    内置函数
    匿名函数
  • 原文地址:https://www.cnblogs.com/Zinn/p/9620379.html
Copyright © 2020-2023  润新知