洛谷 P2161 [SHOI2009]会场预约 stl/线段树

题目链接

一:stl大法好;

考虑把有重合部分的区间重载成相等的,然后利用set去重的性质来做;

(如果不是手动实现,还真不知道set去重的时候是有选择性的去重,并不是去掉跟插入的那个相等的值,也有直接可能去掉你插入的值,挥泪,不了解实现方法的stl不要直接用qwq)

另外,这个想法贼nb啊(来自洛谷题解)%大佬

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define fastio ios::sync_with_stdio(false);cin.tie(0);

const int maxn=2e5+10;
struct node{
    int l,r;
    bool operator < (const node &a)const{
        return r<a.l;
    }
//重载两个区间重合就是相等
};
set<node>st;

int main()
{
    fastio
    int n;
    cin>>n;
    while(n--){
        char c;
        cin>>c;
        if(c=='A'){
            int l,r;
            cin>>l>>r;
            node nd=node{l,r};
            int ans=0;
            set<node>::iterator it=st.find(nd);//要一个一个删掉set中相等的值
            while(it!=st.end()){
                ans++;
                st.erase(it);
                it=st.find(nd);
            }
            st.insert(nd);
            cout<<ans<<endl;
        }
        else{
            cout<<st.size()<<endl;
        }
    }
	return 0;
}

二:线段树:

每次先把所有重叠的区间都清掉,然后在插入新的区间,用vector存删掉的区间数

#include <bits/stdc++.h>
using namespace std;

const int maxn=2e5+10;
const int n=1e5+10;//线段树的大小
int x[maxn],y[maxn];
int vis[maxn];//标记删除了的区间
int ans=0;//记录区间的个数变化
int tree[maxn<<2],lazy[maxn<<2];
#define imid int mid=(l+r)>>1;
#define rl (rt<<1)
#define rr (rt<<1|1)
vector<int>vc;//存删掉的区间
void down(int rt){//将标记往下推,标记为0时不需要处理
    if(lazy[rt]){
        tree[rl]=tree[rr]=lazy[rt];
        lazy[rl]=lazy[rr]=lazy[rt];
        lazy[rt]=0;
    }
}

void up(int rt){//处理父节点的值
    if(!tree[rl] || !tree[rr]) tree[rt]=tree[rl]+tree[rr];
//至少一边没有值,父节点跟有值的那一边相等
    else if(tree[rl]==tree[rr]) tree[rt]=tree[rl];
//左右孩子相等,父节点的值也跟他们相等
    else tree[rt]=-1;
//左右孩子不等且不为0,父节点一定是-1
}

void query(int a,int b,int l,int r,int rt){//查询区间中有无染过的颜色
    if(a<=l && r<=b && tree[rt]!=-1){//值为-1表示区间中不只一种颜色,所以继续往下查找
        if(tree[rt] && !vis[tree[rt]]){//没有被删除并且在区间中,保存下来然后删除
            vis[tree[rt]]=1;
            vc.push_back(tree[rt]);
        }
        return;
    }
    down(rt);
    imid
    if(a<=mid) query(a,b,l,mid,rl);
    if(b>mid) query(a,b,mid+1,r,rr);
    up(rt);
}

void update(int a,int b,int k,int l,int r,int rt){
//更新值,顺便更新lazy标记,此时区间中一定没有别的值
    if(a<=l && r<=b){
        tree[rt]=lazy[rt]=k;
        return;
    }
    down(rt);
    imid
    if(a<=mid) update(a,b,k,l,mid,rl);
    if(b>mid) update(a,b,k,mid+1,r,rr);
    up(rt);
}

int main()
{
    memset(vis,0,sizeof(vis));
    memset(tree,0,sizeof(tree));
    memset(lazy,0,sizeof(lazy));
    int t;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){//把i当成他们的颜色号
        char c;
        scanf(" %c",&c);//前面有空格,不会接收奇奇怪怪的符号
        if(c=='A'){
            ans++;//区间个数
            scanf("%d%d",&x[i],&y[i]);
            vc.clear();
            query(x[i],y[i],1,n,1);//查询区间中的颜色
            for(int j=0;j<vc.size();j++)  update(x[vc[j]],y[vc[j]],0,1,n,1);//删除别的颜色
            update(x[i],y[i],i,1,n,1);//将此区间染色
            printf("%d\n",(int)vc.size());//删除的数目
            ans-=vc.size();//更新区间个数
        }
        else printf("%d\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值