• 【LOJ2586】【APIO2018】选圆圈 CDQ分治 扫描线 平衡树


    题目描述

      在平面上,有 (n) 个圆,记为 (c_1,c_2,ldots,c_n) 。我们尝试对这些圆运行这个算法:

    1. 找到这些圆中半径最大的。如果有多个半径最大的圆,选择编号最小的。记为 (c_i)
    2. 删除 (c_i) 及与其有交集的所有圆。两个圆有交集当且仅当平面上存在一个点,这个点同时在这两个圆的圆周上或圆内。(原文直译:如果平面上存在一个点被这两个圆所包含,我们称这两个圆有交集。一个点被一个圆包含,当且仅当它位于圆内或圆周上。)
    3. 重复上面两个步骤直到所有的圆都被删除。

      当 (c_i) 被删除时,若循环中第1步选择的圆是 (c_j) ,我们说 (c_i) 被 (c_j) 删除。对于每个圆,求出它是被哪一个圆删除的。

      (nleq 300000)

    题解

      貌似不太好枚举每个圆,找出剩下的和这个圆相交的圆。

      那就换一种思路。

      枚举每个圆 (i),找出第一个与这个圆相交且是作为最大的圆被删掉的圆。

      前面的作为最大的圆被删掉的圆肯定是两两不相交的,且半径大于圆 (c_i)

      那么我们可以对前面的圆维护扫描线,每个圆和当前的直线 (x=x_0) 相交两次,可以用括号表示 。

      而且由于这些圆两两不相交,括号相对次序不会变。

      由于前面的圆半径都比它大,因此和它有交的圆必然经过 (x=x_i+r_i)(x=x_i-r_i)(y=y_i-r_i)(y=y_i+r_i)

      所以我们可以在做扫描线时,查询这四个位置的平衡树上,当前圆的前驱后继。

      但是这道题有很多个询问。

      那就加上一个CDQ分治咯。

      时间复杂度:(O(nlog^2n))

      实际上跑的比 k-d tree 还慢很多倍。

    代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cstdlib>
    #include<ctime>
    #include<utility>
    #include<cmath>
    #include<functional>
    #include<set>
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,ll> pll;
    void sort(int &a,int &b)
    {
    	if(a>b)
    		swap(a,b);
    }
    void open(const char *s)
    {
    #ifndef ONLINE_JUDGE
    	char str[100];
    	sprintf(str,"%s.in",s);
    	freopen(str,"r",stdin);
    	sprintf(str,"%s.out",s);
    	freopen(str,"w",stdout);
    #endif
    }
    int rd()
    {
    	int s=0,c,b=0;
    	while(((c=getchar())<'0'||c>'9')&&c!='-');
    	if(c=='-')
    	{
    		c=getchar();
    		b=1;
    	}
    	do
    	{
    		s=s*10+c-'0';
    	}
    	while((c=getchar())>='0'&&c<='9');
    	return b?-s:s;
    }
    void put(int x)
    {
    	if(!x)
    	{
    		putchar('0');
    		return;
    	}
    	static int c[20];
    	int t=0;
    	while(x)
    	{
    		c[++t]=x%10;
    		x/=10;
    	}
    	while(t)
    		putchar(c[t--]+'0');
    }
    int upmin(int &a,int b)
    {
    	if(b<a)
    	{
    		a=b;
    		return 1;
    	}
    	return 0;
    }
    int upmax(int &a,int b)
    {
    	if(b>a)
    	{
    		a=b;
    		return 1;
    	}
    	return 0;
    }
    const int N=300010;
    const int inf=0x7f7f7f7f;
    struct circle
    {
    	ll x,y,r;
    	int id;
    };
    
    struct event
    {
    	ll t;
    	int op;
    	int v;
    	event(){}
    	event(ll a,int b,int c)
    	{
    		t=a;
    		op=b;
    		v=c;
    	}
    };
    
    int cmp(circle a,circle b)
    {
    	if(a.r!=b.r)
    		return a.r>b.r;
    	return a.id<b.id;
    }
    
    int cmp2(event a,event b)
    {
    	return a.t<b.t;
    }
    
    int n;
    circle a[N];
    int ans[N];
    int final[N];
    int b[N];
    int m;
    event c[2*N];
    set<pii> s;
    
    int inter(int x,int y)
    {
    	return (a[x].x-a[y].x)*(a[x].x-a[y].x)+(a[x].y-a[y].y)*(a[x].y-a[y].y)<=(a[x].r+a[y].r)*(a[x].r+a[y].r);
    }
    
    void solve(int l,int r)
    {
    	if(l==r)
    	{
    		if(ans[l]==inf)
    		{
    			ans[l]=l;
    			b[l]=1;
    		}
    		return;
    	}
    	int mid=(l+r)>>1;
    	solve(l,mid);
    	
    	m=0;
    	for(int i=l;i<=mid;i++)
    		if(b[i])
    		{
    			c[++m]=event(3*(a[i].x-a[i].r)-2,1,i);
    			c[++m]=event(3*(a[i].x+a[i].r),2,i);
    		}
    	for(int i=mid+1;i<=r;i++)
    	{
    		c[++m]=event(3*(a[i].x-a[i].r)-1,3,i);
    		c[++m]=event(3*(a[i].x+a[i].r)-1,3,i);
    	}
    	sort(c+1,c+m+1,cmp2);
    	for(int i=1;i<=m;i++)
    		if(c[i].op==1)
    			s.insert(pii(a[c[i].v].y,c[i].v));
    		else if(c[i].op==2)
    			s.erase(pii(a[c[i].v].y,c[i].v));
    		else
    		{
    			auto it=s.lower_bound(pii(a[c[i].v].y,0));
    			if(it!=s.end())
    			{
    				int x=it->second;
    				if(inter(x,c[i].v))
    					ans[c[i].v]=min(ans[c[i].v],x);
    			}
    			if(it!=s.begin())
    			{
    				it--;
    				int x=it->second;
    				if(inter(x,c[i].v))
    					ans[c[i].v]=min(ans[c[i].v],x);
    			}
    		}
    		
    		
    	m=0;
    	for(int i=l;i<=mid;i++)
    		if(b[i])
    		{
    			c[++m]=event(3*(a[i].y-a[i].r)-2,1,i);
    			c[++m]=event(3*(a[i].y+a[i].r),2,i);
    		}
    	for(int i=mid+1;i<=r;i++)
    	{
    		c[++m]=event(3*(a[i].y-a[i].r)-1,3,i);
    		c[++m]=event(3*(a[i].y+a[i].r)-1,3,i);
    	}
    	sort(c+1,c+m+1,cmp2);
    	for(int i=1;i<=m;i++)
    		if(c[i].op==1)
    			s.insert(pii(a[c[i].v].x,c[i].v));
    		else if(c[i].op==2)
    			s.erase(pii(a[c[i].v].x,c[i].v));
    		else
    		{
    			auto it=s.lower_bound(pii(a[c[i].v].x,0));
    			if(it!=s.end())
    			{
    				int x=it->second;
    				if(inter(x,c[i].v))
    					ans[c[i].v]=min(ans[c[i].v],x);
    			}
    			if(it!=s.begin())
    			{
    				it--;
    				int x=it->second;
    				if(inter(x,c[i].v))
    					ans[c[i].v]=min(ans[c[i].v],x);
    			}
    		}
    		
    	solve(mid+1,r);
    }
    
    int main()
    {
    	open("circle");
    	scanf("%d",&n);
    	ll minx=0x7fffffff,miny=0x7fffffff;
    	for(int i=1;i<=n;i++)
    	{
    //		scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
    		a[i].x=rd();
    		a[i].y=rd();
    		a[i].r=rd();
    		a[i].id=i;
    		minx=min(minx,a[i].x);
    		miny=min(miny,a[i].y);
    	}
    	for(int i=1;i<=n;i++)
    	{
    		a[i].x=a[i].x-minx+1;
    		a[i].y=a[i].y-miny+1;
    	}
    	sort(a+1,a+n+1,cmp);
    	memset(ans,0x7f,sizeof ans);
    	solve(1,n);
    	for(int i=1;i<=n;i++)
    		final[a[i].id]=a[ans[i]].id;
    	for(int i=1;i<=n;i++)
    		printf("%d ",final[i]);
    	printf("
    ");
    	return 0;
    }
    
  • 相关阅读:
    如何通過編程獲取列表項目的附件以及多行文本中的文件內容
    小技巧:如何管理保存在本地的用户凭据
    WF 4.0中如何实现xaml工作流的动态加载
    使用jquery构造自己的多级菜单
    和安蕾尔的合影
    360太tmd脑残了
    3D 打印机技术设想
    再放2张数字油画
    解决问题的艺术:半小时编程实现照片的反转负冲特效
    承接数字油画图稿/线条图定制(出图)业务
  • 原文地址:https://www.cnblogs.com/ywwyww/p/9083228.html
Copyright © 2020-2023  润新知