显然,我们可以将每个东西的 $a,b$ 属性转换成二维坐标系中的点.
那么我们每次查询的时候查的是一个右下角矩阵.
没有被套的数量等于矩阵内总数量减去矩阵内可以套其他物品的数量.
我们考虑按照 $a$ 从大到小依次处理.
那么对于 $(x,y)$ 来说,显然匹配一个 $(x',y')$ 满足 $y'$ 是大于 $y$ 中最小的是最优的.
这个过程用线段树维护就行.
code:
#include <bits/stdc++.h>
#define ll long long
#define lson now<<1
#define rson now<<1|1
#define N 400006
#define inf 1000000000
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,Q,B[N];
struct data
{
int a,b,opt,id;
bool operator<(const data b) const { return a==b.a?opt<b.opt:a>b.a;}
}da[N];
struct node
{
int minn,sum;
node() { minn=inf,sum=0; }
node operator+(const node b) const
{
node c;
c.sum=sum+b.sum;
c.minn=min(minn,b.minn);
return c;
}
}s[N<<2];
void update(int l,int r,int now,int p,int v)
{
s[now].sum+=v;
if(l==r)
{
if(s[now].sum)
s[now].minn=l;
else
s[now].minn=inf;
return;
}
int mid=(l+r)>>1;
if(p<=mid)
update(l,mid,lson,p,v);
else
update(mid+1,r,rson,p,v);
s[now]=s[lson]+s[rson];
}
node query(int l,int r,int now,int L,int R)
{
if(l>=L&&r<=R)
return s[now];
int mid=(l+r)>>1;
if(L<=mid&&R>mid)
return query(l,mid,lson,L,R)+query(mid+1,r,rson,L,R);
else if(L<=mid)
return query(l,mid,lson,L,R);
else
return query(mid+1,r,rson,L,R);
}
int Ans[N];
int main()
{
// setIO("input");
int i,j,tot=0;
scanf("%d%d",&n,&Q);
for(i=1;i<=n;++i)
scanf("%d%d",&da[i].a,&da[i].b),da[i].opt=0,B[i]=da[i].b;
tot=n;
for(i=1;i<=Q;++i)
{
++tot;
da[tot].id=i;
scanf("%d%d",&da[tot].a,&da[tot].b),da[tot].opt=1,B[tot]=da[tot].b;
}
sort(B+1,B+1+tot);
for(i=1;i<=tot;++i)
da[i].b=lower_bound(B+1,B+1+tot,da[i].b)-B;
sort(da+1,da+1+tot);
for(i=1;i<=tot;i=j)
{
for(j=i;da[j].a==da[i].a&&j<=tot;++j);
for(int k=i;k<j;++k)
{
// printf("%d\n",k);
if(da[k].opt==0)
{
node cu=query(1,tot,1,da[k].b+1,tot);
if(cu.minn!=inf)
update(1,tot,1,cu.minn,-1);
}
}
for(int k=i;k<j;++k)
{
if(da[k].opt==0)
update(1,tot,1,da[k].b,1);
}
for(int k=i;k<j;++k)
{
if(da[k].opt==1)
Ans[da[k].id]=query(1,tot,1,1,da[k].b).sum;
}
}
for(i=1;i<=Q;++i)
printf("%d\n",Ans[i]);
return 0;
}