【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\) 刪除。對於每個圓,求出它是被哪一個圓刪除的。

  \(n\leq 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(n\log^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("\n");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM