题目地址:http://codeforces.com/contest/369/problem/E
看完题目,很明显是离散+树状数组的思路,然而并没有想到怎么离散。这题的解法实在巧妙。
这题要求的是至少有一个点包含的线段个数,可以利用容斥思想,找没有一个点包含的线段个数,用总个数减去它既可。这时还是不好求。
这里就有一个巧妙的方法,求点的补集,即一堆线段,然后求这些线段完全包含的给定的线段的个数,因为如果完全包含的话,那么就不可能会去占据那点,相反,如果不包含的话,则肯定会占至少一个点。然后把线段按右端点排序降维,用树状数组找左端点个数既可。
代码如下:
#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
#include <algorithm>
#include <stdlib.h>
#include <map>
#include <set>
#include <stdio.h>
#include <string>
#include <time.h>
using namespace std;
#define LL __int64
#define pi acos(-1.0)
#pragma comment(linker, "/STACK:1024000000")
const int mod=9901;
const int INF=0x3f3f3f3f;
const double eqs=1e-9;
const int MAXN=300000+10;
int ans[MAXN], c[1000010], a[MAXN];
struct node
{
int l, r, id;
bool operator < (const node &tmp) const{
if(r==tmp.r) return l<tmp.l;
return r<tmp.r;
}
}line1[MAXN], line2[MAXN<<1];
int lowbit(int x)
{
return x&(-x);
}
void add(int x)
{
for(int i=x;i<=1000000;i+=lowbit(i)){
c[i]++;
}
}
int sum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i)){
ans+=c[i];
}
return ans;
}
int main()
{
int n, m, i, j, cnt, y;
while(scanf("%d%d",&n,&m)!=EOF){
for(i=0;i<n;i++){
scanf("%d%d",&line1[i].l,&line1[i].r);
line1[i].id=0;
}
memset(c,0,sizeof(c));
sort(line1,line1+n);
cnt=0;
for(i=0;i<m;i++){
scanf("%d",&y);
ans[i]=n;
for(j=0;j<y;j++){
scanf("%d",&a[j]);
if(!j&&a[j]!=1) {
line2[cnt].l=1;
line2[cnt].r=a[j]-1;
line2[cnt++].id=i;
}
if(j==y-1&&a[j]!=1000000) {
line2[cnt].l=a[j]+1;
line2[cnt].r=1000000;
line2[cnt++].id=i;
}
if(j&&a[j]!=a[j-1]+1){
line2[cnt].l=a[j-1]+1;
line2[cnt].r=a[j]-1;
line2[cnt++].id=i;
}
}
}
sort(line2,line2+cnt);
j=0;
for(i=0;i<cnt;i++){
while(j<n&&line1[j].r<=line2[i].r){
add(line1[j].l);
j++;
}
ans[line2[i].id]-=j-sum(line2[i].l-1);
}
for(i=0;i<m;i++){
printf("%d\n",ans[i]);
}
}
return 0;
}