一、题目
二、解法
典型的正难则反,问题可以转化为有多少线段“夹在”给定的两个点之间(补齐最左边和最右边),那么拿总的线段数减去即是答案。
现在问题还是不好解决,因为判断包含需要两个量(左端点和右端点),如果消去一个量就会好很多。可以把问题离线下来,和给出的线段放在一起排序,左端点从大到小,相同的话给出线段在前,然后维护一个右端点的树状数组,就只用查右端点了。
#include <cstdio>
#include <algorithm>
using namespace std;
const int M = 300005;
#define up 1000000
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,b[M],ans[M],bit[up+5];
struct node
{
int l,r,id,op;
bool operator < (const node &b) const
{
if(l==b.l) return op<b.op;
return l>b.l;
}
}a[3*M];
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
for(;x<=up;x+=lowbit(x))
bit[x]++;
}
int sum(int x)
{
int s=0;
for(;x>=1;x-=lowbit(x))
s+=bit[x];
return s;
}
signed main()
{
n=read();m=read();k=n;
for(int i=1;i<=n;i++)
{
int l=read(),r=read();
a[i]=node{l,r,i,0};
}
for(int w=1;w<=m;w++)
{
int t=read();b[0]=0;
for(int i=1;i<=t;i++)
{
b[i]=read();
if(b[i]>b[i-1]+1)
a[++n]=node{b[i-1]+1,b[i]-1,w,1};
}
a[++n]=node{b[t]+1,up,w,1};
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
if(a[i].op==0) add(a[i].r);
else ans[a[i].id]+=sum(a[i].r);
}
for(int i=1;i<=m;i++)
printf("%d\n",k-ans[i]);
}