【XSY3904】直线(分块)

题面

直线

题解

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

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

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

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

分析一下时间复杂度:

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

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

总时间复杂度 O ( n B + q n B ) O(nB+q\dfrac{n}{B}) O(nB+qBn),取 B = n B=\sqrt{n} B=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\n",ans);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值