【XSY3904】直线(分块)

题面

直线

题解

注意到题目没有给什么特殊的性质,除了直线随机生成。

所以考虑随机化算法或均摊算法。

有一种很神奇的分块做法:

考虑将整个平面分成 (B imes B) 块,每块大小 (dfrac{10^9}{B} imesdfrac{10^9}{B}),而且每一个块记录一下有哪些直线经过它,询问的时候直接枚举经过询问点所在块内的所有直线并判断统计。

分析一下时间复杂度:

首先是预处理,对于每一条直线,它经过的块数大约为 (B),所以这部分的时间复杂度是 (O(nB))

其次是询问时枚举当前块内的所有直线。刚刚我们得出了所有直线经过的块数为 (O(nB)) 级别的,那么每一块均摊下来经过直线条数约是:(dfrac{nB}{B^2}=dfrac{n}{B}),所以这部分的时间复杂度是 (O(qdfrac{n}{B}))

总时间复杂度 (O(nB+qdfrac{n}{B})),取 (B=sqrt{n}) 即可。

代码如下:

#include<bits/stdc++.h>

#define SN 350
#define N 100010
#define eps 1e-6

using namespace std;

inline 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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,q,x[N][2],y[N][2];
int block,len;

vector<int>v[SN][SN];

bool compare(double a,double b)
{
	if(fabs(a-b)<eps) return 0;
	return (a<b?-1:1);
}

int get(double x)
{
	if(!compare(x,(int)x))
		return ((int)x-1)/len+1;
	return ((int)ceil(x)-1)/len+1;
}

int main()
{
	n=read(),q=read();
	block=sqrt(n);
	len=1e9/block;
	while(block*len<1e9) len++;
	int check=1;
	get(check);
	for(int i=1;i<=n;i++)
	{
		x[i][0]=read(),y[i][0]=read(),x[i][1]=read(),y[i][1]=read();
		if(x[i][0]==x[i][1])
		{
			int b=get(x[i][0]);
			for(int j=1;j<=block;j++)
				v[b][j].push_back(i);
		}
		else if(y[i][0]==y[i][1])
		{
			int b=get(y[i][0]);
			for(int j=1;j<=block;j++)
				v[j][b].push_back(i);
		}
		else
		{
			double k=1.0*(y[i][1]-y[i][0])/(x[i][1]-x[i][0]);
			double b=1.0*y[i][0]-k*x[i][0];
			double l=min(1e9,max(1.0,(1e9-b)/k)),r=max(1.0,min(1e9,(1-b)/k));
			if(l>r) swap(l,r);
			int xlb=get(l),xrb=get(r);
			for(int j=xlb;j<=xrb;j++)
			{
				double xl=max(l,1.0*(j-1)*len+1),xr=min(r,1.0*j*len);
				double yl=k*xl+b,yr=k*xr+b;
				if(yl>yr) swap(yl,yr);
				int ylb=get(yl),yrb=get(yr);
				for(int k=ylb;k<=yrb;k++)
					v[j][k].push_back(i);
			}
		}
	}
	while(q--)
	{
		int xx=read(),yy=read();
		int xb=get(xx),yb=get(yy),ans=0;
		for(int i=0,size=v[xb][yb].size();i<size;i++)
		{
			int k=v[xb][yb][i];
			ans+=(1ll*(y[k][1]-y[k][0])*(xx-x[k][1])==1ll*(yy-y[k][1])*(x[k][1]-x[k][0]));
		}
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ez-lcw/p/14449397.html