题目链接:https://www.luogu.com.cn/problem/P2161
本题有好多做法,什么平衡树呀,STL呀,无奈蒟蒻的我只能用线段树染色来做。
每次A操作,把一个区间给染色了,如果那个区间之前被染色过,就要被删除,这里我们不直接删除,而是用 del数组 标记一下该颜色已被删除即可。我们需要在标准区间更新线段的基础加上一个 update 函数,当我们要染色的区间包含了当前区间,就用 update 函数将该区间全部染成新颜色,并更新总颜色和删除颜色的数量。我们还要用 vis 数组判断一个区间是否是同一种颜色,如果是同一种颜色,即可直接染色,如果不是而要继续向下找。注意,标记下传的时候,别忘了把原来颜色给改为无色,防止以后重复下传(有wa的经历 qwq)。
B操作,直接输出
代码如下
#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+5;
int tree[maxn];//子树的颜色
bool vis[maxn];//子树是否具有相同颜色
bool del[maxn];//该颜色是否被删除
char opt[2];//选择
int st,ed;//区间头与尾
int ans,era;//未被删除的颜色数量
//被删除的颜色数量
int cnt;//颜色编号
void push_down(int l,int r,int k)//标记下传
{
vis[k]=false;//若是下传了,
//说明子树肯定不是相同颜色
//(因为新增了一种颜色)
if(!tree[k])
return ;//无色不用下传
tree[k<<1]=tree[k<<1|1]=tree[k];//下传颜色
tree[k]=0;//标记已下传
//改为无色,防止以后重复下传
}
void build(int l,int r,int k)//建树
{
vis[k]=true,tree[k]=0;//初始全都为一个颜色
//即无色(==0)
if(l==r)
return ;
int mid=(l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
}
void update(int l,int r,int k)//染色
{
if(vis[k])//子树颜色相同,直接染色
{
if(!del[tree[k]]&&tree[k])
//未被删除且不是无色
ans--,era++;//总颜色-1,删除颜色+1
del[tree[k]]=true;//删除该颜色
tree[k]=cnt;//染色
return ;
}
int mid=(l+r)>>1;
update(l,mid,k<<1);
update(mid+1,r,k<<1|1);
vis[k]=1,tree[k]=cnt;//该区间全染成新颜色
}
void modify(int l,int r,int k)//区间更新
{
if(l>=st&&ed>=r)
//该区间要全部染成新颜色
{
update(l,r,k);//染色
return ;
}
push_down(l,r,k);
int mid=(l+r)>>1;
if(st<=mid)
modify(l,mid,k<<1);
if(ed>mid)
modify(mid+1,r,k<<1|1);
}
int main()
{
int n;//操作次数
scanf("%d",&n);
build(1,100000,1);//建树
for(int i=1;i<=n;i++)
{
scanf("%s",opt);
if(opt[0]=='B')
{
printf("%d\n",ans);
}
else
{
scanf("%d %d",&st,&ed);
cnt++,ans++;//新增了一种颜色,数量+1,编号+1
era=0;//此次操作删除颜色数量初始为0
modify(1,100000,1);
printf("%d\n",era);
}
}
return 0;
}