转载注明出处 http://blog.youkuaiyun.com/moedane
传送门 http://codeforces.com/contest/369/problem/E
题意
给出n条线段,以及m个询问,每个询问包括cnt个点,问有多少条线段覆盖了至少一个点。
思路
我一开始是正着思考,想着怎样直接统计出覆盖的线段就好了。这样想的话要解决一个重复的问题,即一个线段覆盖了多个点。想直接用前缀和后缀和处理,但是样例都没法过,发现有许多问题。最后去看了别人代码才知道解法。
对每个询问,找出一个点都不覆盖的线段的数目。
要找出一个点都不覆盖的线段,方法是,用结构体存储询问的当前点和它左边的点,把落在该区域内的线段数目统计出来即可。对于每个询问,最左端端点可设为0,最右端可设为10^6,即问题转化为求落在这些区间内的线段的数目。
至于求这些线段数目的方法,是限制右端点小于当前询问的右端点,左端点则更新到树状数组中,即可找到当前区间内的线段数目。
先将线段排好序,离线处理询问。
代码
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <cctype>
#define bug puts("here");
using namespace std;
typedef long long ll;
const int maxn = 3 * 100086;
const int mod = 1000000007;
const double PI = atan(1.0)*4.0;
int f[1000086];
int Ans[maxn];
struct Node
{
int l,r,index;
}s[maxn],q[maxn*2];
bool cmp(Node a,Node b)
{
return a.r < b.r;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int p,int v)
{
while(p < 1000006)
{
f[p] += v;
p += lowbit(p);
}
return;
}
int sum(int p)
{
int ans = 0;
while(p)
{
ans += f[p];
p -= lowbit(p);
}
return ans;
}
int main()
{
int m,n;
scanf("%d%d",&n,&m);
int i,j;
for(i=0;i<n;i++)
scanf("%d%d",&s[i].l,&s[i].r);
int e = 0;
for(i=0;i<m;i++)
{
int cnt;
scanf("%d",&cnt);
for(j=0;j<cnt;j++)
{
scanf("%d",&q[e].r);
q[e].index = i;
if(j == 0) q[e].l = 0;
else q[e].l = q[e-1].r;
e++;
}
q[e].index = i;
q[e].l = q[e-1].r;
q[e++].r = 1000006;
}
sort(s,s+n,cmp);
sort(q,q+e,cmp);
for(i=0;i<m;i++) Ans[i] = n;
memset(f,0,sizeof(f));
j = 0;
for(i=0;i<e;i++)
{
while(j < n && s[j].r < q[i].r)
{
update(s[j].l,1);
j++;
}
Ans[q[i].index] -= sum(q[i].r - 1) - sum(q[i].l);
}
for(i=0;i<m;i++) printf("%d\n",Ans[i]);
return 0;
}