set与multiset的妙用

一,概述

set,不可重结合(保序)

  • 底层实现: 红黑树
  • 插入复杂度: O ( l o g N ) O(log N) O(logN)
  • 优势:在只须前驱后继时和大量平衡树开设需求时

multiset 可重集(保序)

二,例题

P5250 【深基17.例5】木材仓库

一个木材仓库,里面可以存储各种长度的木材,但是保证没有两个木材的长度是相同的。
作为仓库负责人,你有时候会进货,有时候会出货,因此需要维护这个库存。有不超过 100000 条的操作:

  • 进货,格式1 Length:在仓库中放入一根长度为 Length(不超过 1 0 9 10^9 109) 的木材。如果已经有相同长度的木材那么输出Already Exist
  • 出货,格式2 Length:从仓库中取出长度为 Length 的木材。如果没有刚好长度的木材,取出仓库中存在的和要求长度最接近的木材。如果有多根木材符合要求,取出比较短的一根。
    输出取出的木材长度。如果仓库是空的,输出Empty
#include<bits/stdc++.h>
using namespace std;
set<int>::iterator it1,it2 ;
set<int>s;

int main()
{
    int op,n,x;
    cin>>n;
    while(n--)
    {
        cin>>op>>x;
        if(op==1)
        {
            if(!s.insert(x).second)puts("Already Exist");
        }
        else
        {
            if(s.empty()){puts("Empty");continue;}
            if(s.find(x)!=s.end()){cout<<x<<endl;s.erase(s.find(x));continue;}

            it1=it2=s.lower_bound(x);
            if(it1==s.begin())cout<<*it1,s.erase(it1);
            else if(it1==s.end())cout<<*(-- it1),s.erase( it1);
            else
            {
                it2 --;
                if(*it1-x<x-*it2)cout<<*it1,s.erase(it1);
                else cout<<*it2,s.erase(it2);

            }

            puts("");
        }
    }
}

P2161 [SHOI2009]会场预约

https://www.luogu.com.cn/problem/P2161

你需要维护一个 在数轴上的线段 的集合 SS,支持两种操作:

  • A l r 表示将 S S S 中所有与线段 [ l , r ] [ l , r ] [l,r][l,r] [l,r][l,r] 相交的线段删去,并将 [ l , r ] [ l , r ] [l,r][l,r] [l,r][l,r] 加入 S S S 中。
  • B 查询 S S S 中的元素数量。
  • 对于 A 操作,每次还需输出删掉的元素个数。
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i= j;i<=k;i++)
using namespace std;
typedef long long ll;
const int N= 1e6+9;
struct seg {int l,r; };
bool operator < (const seg&a ,const seg&b) { return a.r<b.l; }

set<seg>s;


signed main()
{
    int t;
    scanf("%d",&t);
    while (t--)
    {
        char op[2];
        int x,y;
        scanf("%s",op);

        if(*op=='A')
        {
            scanf("%d%d",&x,&y);
            seg tem= {x,y};
            int cnt= 0;
            set<seg> :: iterator it;

            it=s.find(tem);
            while (it != s.end())
            {
                s.erase(it);
                cnt++;
                it= s.find(tem);
            }
            s.insert(tem);
            printf("%d\n",cnt);
        }

        else printf("%d\n",s.size());
    }
    return 0;
}

P1110 [ZJOI2007]报表统计

https://www.luogu.com.cn/problem/P1110

在最开始的时候,有一个长度为 n 的整数序列 a,并且有以下三种操作:

INSERT i k:在原数列的第 i 个元素后面添加一个新元素 k;如果原数列的第 i 个元素已经添加了若干元素,则添加在这些元素的最后(见样例说明)。
MIN_GAP:查询相邻两个元素的之间差值(绝对值)的最小值。
MIN_SORT_GAP:查询所有元素中最接近的两个元素的差值(绝对值)。

#include<bits/stdc++.h>
using namespace std;
const int N= 1e6+8;
const int INF = 1e9;
multiset<int>full,delta;
int ans;
int n,m,q;
int x,z,y;
int st[N],ed[N];

void up_ans(int u)
{
    auto it = full.lower_bound(u);
    int nw = (*it-u);
    --it;
    nw = min(nw,u-*it);
    ans= min(ans,nw);
    full.insert(u);
}

void replac(int pos,int x)
{
    delta.insert( abs( x - ed[pos] ) );
    if( pos != n )
        delta.erase( delta.find( abs( st[pos+1] - ed[pos] ) ) ),
        delta.insert( abs( st[pos+1] - x ) );
    ed[pos] = x;
}

signed main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        int u;
        scanf("%d",&u);
        st[i]=ed[i]=u;
    }
    ans = INF;
    full.insert(INF);
    full.insert(-INF);
    for(int i=1;i<n;i++) delta.insert(abs(st[i+1]-ed[i]));
    for(int i=1;i<=n;i++) up_ans(st[i]);

    while (m--)
    {
        char op[20];
        int pos,val;
        scanf("%s",op);
        if(op[4]=='R')
        {
            scanf("%d%d",&pos,&val);
            up_ans(val);
            replac(pos,val);
        }

        else if(op[4]=='G') printf("%d\n",*delta.begin());
        else printf ("%d\n",ans);
    }
    return 0;
}

树套树简单版

一种数据结构,来维护一个长度为 n 的序列,其中需要提供以下操作:

  • 1 pos x,将 pos 位置的数修改为 x
  • 2 l r x,查询整数 x 在区间 [l,r] 内的前驱(前驱定义为小于 x,且最大的数)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>

using namespace std;
const int N = 2e5+100;
const int INF = 1e9; 
struct node 
{
    int l,r;
    multiset<int>s;
}tr[N];
int w[N];

void build (int u,int l,int r)
{
    tr[u]={l,r};
    tr[u].s.insert(INF);
    tr[u].s.insert(-INF);
    for (int i = l; i <= r; i ++ ) tr[u].s.insert(w[i]);
    if(l==r) return ;
    int mid = (l+r)>>1;
    build (u<<1,l,mid);
    build (u<<1|1,mid+1,r);
}

void modify(int u,int pos,int val)
{
    auto j = tr[u].s.find(w[pos]);
    tr[u].s.erase(j);
    tr[u].s.insert(val);
    if(tr[u].l==tr[u].r && tr[u].l==pos) return;
    int mid = tr[u].l+tr[u].r>>1;
    if(pos<=mid)modify(u<<1,pos,val);
    else modify(u<<1|1,pos,val);
}


int query(int u,int a,int b,int x)
{
    if(tr[u].l >= a && tr[u].r <= b)
    {
        auto it = tr[u].s.lower_bound(x);
        it--;
        return *it;
    }
    int mid = tr[u].l+tr[u].r >> 1;
    int res  = -INF;
    if(a<=mid) res=max(res,query(u<<1,a,b,x));
    if(b>mid) res=max(res,query(u<<1|1,a,b,x));
    return res;
}


int main()
{
    int n,m;
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) cin >> w[i];
    build (1,1,n);
    while (m -- )
    {
        int op,x,y,val;
        scanf("%d%d%d",&op,&x,&y);
        if(op==1) modify(1,x,y), w[x]=y;
        else scanf("%d",&val),printf("%d\n",query(1,x,y,val));
    }
    
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流苏贺风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值