• 【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;
    }
    
  • 相关阅读:
    UVALive 4764 简单dp水题(也可以暴力求解)
    poj 2151 概率DP(水)
    poj 2299 归并排序求逆序数 (可做模板)
    poj2388 更水
    poj1936 假期计划第一水
    poj 3080 kmp求解多个字符串的最长公共字串,(数据小,有点小暴力 16ms)
    UVA315:Network(求割点)
    POJ1236:Network of Schools (思维+Tarjan缩点)
    SPOJ
    HDU4305:Lightning(生成树计数+判断点是否在线段上)
  • 原文地址:https://www.cnblogs.com/ez-lcw/p/14449397.html
Copyright © 2020-2023  润新知