先说一下这道题的解题思路,我们使用离线操作的做法。记录读入的数据,把开始结束日期存在结构体里,并记录是A操作还是B操作。开一个数组记录染色情况、一个cnt数组记录第几个操作推掉了几个预约,读入后我们倒着循环进行查找,如果读入的是B就跳过,如果是A的话就将开始到结束日期的所有点染成当前序号的颜色。如果循环到某段日期它已经有染色标记了,就将当前那段日期所对应的的染色标记的最小值的推掉的预约数+1(因为是倒着来的)(较小的那个标记:明明是我先来的)。最后正着输出答案,如果是A操作就输出当前的cnt,在主函数开一个变量ans记录B操作的答案,并且A操作结束后ans要加上1减去cnt[i](新增了1个预约,推掉了cnt[i]个预约)。
显然 数据范围拒绝我们用暴力的方法解决染色和查找最小值的操作,所以我们只能用线段树。线段树的范围为1到100000,开一个数组minn记录某个结点的minn值,初始化为最大值1e9。先写下放标记和最小值的函数,如果父亲结点的标记为0就不下放,否则就用父亲节点的标记覆盖孩子节点的标记和最小值,父亲节点标记清零。然后是染色的函数,这里在染色的过程中注意更新最小值。再是查找区间最小值的函数,线段树写过
#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=200010;
int mark[maxn*8];
int n;
int ans=0;
struct borrow
{
int x;
int y;
}a[maxn];
int b[maxn];
int cnt[maxn];
int minn[maxn*8];
void pushdown(int now,int l,int r)
{
if(mark[now]==0) return;
mark[now*2]=mark[now];
minn[now*2]=mark[now];
mark[now*2+1]=mark[now];
minn[now*2+1]=mark[now];
mark[now]=0;
}
ll getminn(int now,int l,int r,int x,int y)
{
pushdown(now,l,r);
if(x<=l&&y>=r)
{
return minn[now];
}
int mid=(l+r)/2;
ll ans=1e9;
if(x<=mid) ans=min(ans,getminn(now*2,l,mid,x,y));
if(mid+1<=y) ans=min(ans,getminn(now*2+1,mid+1,r,x,y));
return ans;
}
void addnum(int now,int l,int r,int x,int y,int k)
{
pushdown(now,l,r);
//cout<<now<<' '<<l<<' '<<r<<' '<<minn[now]<<endl;
if(x<=l&&y>=r)
{
minn[now]=k;
mark[now]=k;
return ;
}
if(mark[now]==k)
{
return ;
}
int mid=(l+r)/2;
if(x<=mid) addnum(now*2,l,mid,x,y,k);
if(mid+1<=y) addnum(now*2+1,mid+1,r,x,y,k);
minn[now]=min(minn[now*2],minn[now*2+1]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=maxn*8;i++)
{
minn[i]=1e9;
}
for(int i=1;i<=n;i++)
{
char c;
cin>>c;
if(c=='A')
{
b[i]=1; //A
scanf("%d%d",&a[i].x,&a[i].y);
}
if(c=='B')
{
b[i]=2; //B
}
}
for(int i=n;i>=1;i--)
{
if(b[i]==2) continue;
else
{
ll w=getminn(1,1,100000,a[i].x,a[i].y);
if(w!=1e9)
{
cnt[w]++;
}
addnum(1,1,100000,a[i].x,a[i].y,i);
}
}
for(int i=1;i<=n;i++)
{
if(b[i]==1)
{
cout<<cnt[i]<<endl;
ans=ans+1-cnt[i];
}
else
{
cout<<ans<<endl;
}
}
return 0;
}