zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    main函数的一些特性
    确保函数的操作不超出数组实参的边界
    今天学习了一点sed
    libevent 与事件驱动
    mvc3 action验证失败后的自定义处理
    使用spring.net+nibernate时如何用aspnet_regiis加密数据库连接字符串
    C# 中 IList IEnumable 转换成 List类型
    Nhibernate 过长的字符串报错 dehydration property
    小论接口(interface)和抽象类(abstract class)的区别
    C# 语言在函数参数列表中出现this关键词的作用
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14449397.html
Copyright © 2011-2022 走看看