CodeForces - 369E Valera and Queries
题目大意:给出n个线段(线段的左端点和右端点坐标)和m个查询,每个查询有cnt个点,要求给出有多少条线段包含至少其中一个点。
思路:如果按照题意正面去算有每个线段是否包含点,那么时间复杂度是不允许的;如果去算每个点被哪些线段包含,那么去重比较困难。正难则反,因此对于每个查询,选择去求有多少个线段没有覆盖任何一个点,那么答案则为n减所求。用树状数组来统计没有覆盖任何一个点的线段数,大致做法是将临界线段(如x1+1,x2-1)插入seg数组中,再按一定规则进行排序,之后通过树状数组维护统计被包含于临界线段的线段数,具体实现在代码中。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1000000+5;
struct segment
{
int l,r,id;
};
bool cmp(const segment& a,const segment& b)
{
if (a.l!=b.l)
return a.l>b.l;
if (a.r!=b.r)
return a.r<b.r;
return a.id<b.id;
}
segment seg[maxn];
int tree[maxn+5];
void add(int i,int x)
{
while (i<=maxn)
{
tree[i]+=x;
i+=i&-i;
}
return;
}
int sum(int i)
{
int sum=0;
while (i>0)
{
sum+=tree[i];
i-=i&-i;
}
return sum;
}
int ans[maxn];
int main()
{
int n,m,i,j;
cin>>n>>m;
for (i=0;i<n;++i)
{
scanf("%d%d",&seg[i].l,&seg[i].r);
seg[i].id=0;
}
int cnt,x,pre,tt=n;
for (i=1;i<=m;++i)
{
ans[i]=n;
pre=0;
scanf("%d",&cnt);
for (j=0;j<cnt;++j)
{
scanf("%d",&x);
if (pre+1<=x-1)
{
seg[tt].l=pre+1;
seg[tt].r=x-1;
seg[tt++].id=i;
}
pre=x;
}
seg[tt].l=pre+1;
seg[tt].r=maxn;
seg[tt++].id=i;
}
sort(seg,seg+tt,cmp);
for (i=0;i<tt;++i)
{
if (seg[i].id)
{
ans[seg[i].id]-=sum(seg[i].r);
}
else
{
add(seg[i].r,1);
}
}
for (i=1;i<=m;++i)
{
printf("%d\n",ans[i]);
}
return 0;
}