D. Multiset(线段树||树状数组,单点修改)

题目传送门

题意: 给你一个n个数的数组,有q次询问,每一询问一个数k,如果k大于等于0,则把k插入数组中,如果k小于0,则删除数组中第丨k丨大的。最后如果数组中还有数,则随便输出一个数组中的数,否则输出0。

空间特殊要求: 28M。

线段树思路: 线段树维护区间内存在的数的个数,每次插入就向下查询到l==r的时候,sum[p]++,每次删除的时候就判断第k大的数是在左儿子还是右儿子,右儿子的话要查询右儿子中第k-sum[ls]大的数。最后如果sum[1]不等于0的话,说明数组中还有数,向下查询查到非空直接返回,否则输出0。

线段树代码:

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
//#define int long long
#define vi vector<int>
#define mii map<int,int>
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=1e6+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-6;
const double PI=acos(-1);
int sum[N<<2];
void in(int p,int l,int r,int k)
{
    if(l==r)
    {
        sum[p]++;
        return ;
    }
    int mid=l+r>>1;
    if(k<=mid)
        in(ls,l,mid,k);
    else
        in(rs,mid+1,r,k);
    sum[p]=sum[ls]+sum[rs];
}
void del(int p,int l,int r,int k)
{
    if(l==r)
    {
        sum[p]--;
        return ;
    }
    int mid=l+r>>1;
    if(sum[ls]>=k)
        del(ls,l,mid,k);
    else
        del(rs,mid+1,r,k-sum[ls]);
    sum[p]=sum[ls]+sum[rs];
}
int ask(int p,int l,int r)
{
    if(l==r)
    {
        return l;
    }
    int mid=l+r>>1;
    if(sum[ls]!=0)
        return ask(ls,l,mid);
    if(sum[rs]!=0)
        return ask(rs,mid+1,r);
}
signed main()
{
    int n=read(),q=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        in(1,1,n,x);
    }
    while(q--)
    {
        int x=read();
        if(x>0)
            in(1,1,n,x);
        else
        {
            x=-x;
            del(1,1,n,x);
        }
    }
    if(sum[1]!=0)
    {
        cout<<ask(1,1,n)<<endl;
    }
    else
        cout<<0<<endl;
}

树状数组思路: 众所周知,树状数组在某些方面可以代替线段树精简地完成一些操作,二者时间都是log级别的,但是树状数组的常数要比线段树好,空间也比线段树小。查位置的时候用到二分查询,如果前面的总个数小于k,说明第k个在右边,否则在左边,最后查询到了就对这个点进行-1操作。(ask(i)表示数组元素中在1~i内的个数)

树状数组代码

#include<bits/stdc++.h>
#define endl '\n'
#define null NULL
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define ll long long
//#define int long long
#define vi vector<int>
#define mii map<int,int>
#define pii pair<int,int>
#define ull unsigned long long
#define all(x) x.begin(),x.end()
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ct cerr<<"Time elapsed:"<<1.0*clock()/CLOCKS_PER_SEC<<"s.\n";
char *fs,*ft,buf[1<<20];
#define gc() (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<20,stdin),fs==ft))?0:*fs++;
inline int read(){int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=gc();}
return x*f;}
using namespace std;
const int N=1e6+5;
const int inf=0x7fffffff;
const int mod=998244353;
const double eps=1e-6;
const double PI=acos(-1);
int c[N];
int lowbit(int x)
{
    return x&-x;
}
void add(int x,int k)
{
    for(int i=x;i<N;i+=lowbit(i))
        c[i]+=k;
}
int ask(int x)
{
    int res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=c[i];
    return res;
}
signed main()
{
    int n=read(),q=read();
    for(int i=1;i<=n;i++)
    {
        int x=read();
        add(x,1);
    }
    while(q--)
    {
        int x=read();
        if(x>0)
            add(x,1);
        else
        {
            x=-x;
            int l=1,r=1e6;
            while(l<r)
            {
                int mid=l+r>>1;
                if(ask(mid)<x)
                    l=mid+1;
                else
                    r=mid;
            }
            add(r,-1);
        }
    }
    if(ask(N-1))
    {
        for(int i=1;i<=n;i++)
        {
            if(c[i])
            {
                cout<<i<<endl;
                break;
            }
        }
    }
    else
        cout<<0<<endl;
}

猜你喜欢

转载自blog.csdn.net/Joker_He/article/details/106193405
今日推荐