题意:给你多组数据,每组数据第一行是一个n和m,表示的意义是n条线段和m组点。接下来是n条线段的左端点和右端点,m行每行第一个是这组点的个数Ki,接下来同一行有Ki个数表示点的位置。问题是求对于每组点来说,有多少条线段至少有一个该组上的点在线段上,输入m行。
分析:其实题目要求的问题,我们有一种很直接的错误的办法:将每条线段铺在数轴上,然后对于每组点的每个点来数一数有多少条线段经过这个点。很显然我们会重复计算经过多个点的线段,怎么办呢?其实我们只要将前面经过点的线段在后面的计算中去除就行啦。怎么去除呢?我们可以先将每一条线段的左右端点和每一组点的前后两个点当成一组点来存起来,然后排序,排序规则是线(L,R),点(前,后)按照"L"和"前"从小到大排序,“L”=“前"的时候"L"<”前“,这样我们将点和线排完之后。按排序顺序处理点和线段,(1)遇到线段时,我们将该线段的右端点插入到树状数组中。(2)遇到点时,我们将已经插入到树状数组中的右端点大于等于”前“的点的个数加入到ans[i]中,同时将右端点大于等于”后“的点的个数在ans[i]中减去。这样我们就将原来的那个错误的办法处理成正确的方法啦~时间复杂度是O((n+sum)*log10^6),sum=m组点的总个数。
代码:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=1000010;
const int MAX=151;
const int MOD=1000007;
const int MOD1=100000007;
const int MOD2=100000009;
const int INF=1000000000;
const double EPS=0.00000001;
typedef long long ll;
typedef unsigned long long ull;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int e[300010],ans[300010],f[N];
struct node{
int a,b,c,d;
}q[N];
int cmd(node x,node y) {
if (x.a!=y.a) return x.a<y.a;
else return x.c<y.c;
}
void add(int x,int y) {
for (;x<=1000000;x+=x&(-x)) f[x]+=y;
}
int get(int x) {
int ret=0;
for (;x;x-=x&(-x)) ret+=f[x];
return ret;
}
int main()
{
int a,b,i,j,k,g,n,m;
while (scanf("%d%d", &n, &m)!=EOF) {
k=0;
for (i=1;i<=n;i++) {
scanf("%d%d", &a, &b);
k++;q[k].a=a;q[k].b=b;q[k].c=0;q[k].d=0;
}
for (i=1;i<=m;i++) {
scanf("%d", &g);e[g+1]=1000001;
for (j=1;j<=g;j++) scanf("%d", &e[j]);
for (j=1;j<=g;j++) {
k++;q[k].a=e[j];q[k].b=e[j+1];q[k].c=1;q[k].d=i;
}
}
sort(q+1,q+k+1,cmd);
memset(f,0,sizeof(f));
memset(ans,0,sizeof(ans));
for (i=1;i<=k;i++)
if (q[i].c) ans[q[i].d]+=get(q[i].b-1)-get(q[i].a-1);
else add(q[i].b,1);
for (i=1;i<=m;i++) printf("%d\n", ans[i]);
}
return 0;
}