[JZOJ2724] 圆【计算几何】【数据结构】【DP】

29 篇文章 0 订阅
29 篇文章 0 订阅

Description

二维坐标平面内有n个圆,第i个圆圆心在(Xi,Yi),半径为Ri,权值Vi。任何两个圆都不会相交(也不会相切),但是圆与圆之间可能存在包含关系。当我们在一个圆里面的时候,我们必须经过它的边界一次,才能走出这个圆。

对于不同的两个圆A、B,如果可以从A到B且经过不超过K次边界,则称A、B是连通的。现在的问题是:对于所有的连通的一对圆(A,B),权值差的绝对值最大有多少。即找到max{ |VA – VB|, 其中A B连通}。

对于100%的数据,T ≤ 10, 2 ≤ n ≤ 50000, 2 ≤ K ≤ 100, 0 < Ri ≤ 200000000, -1000000000 ≤ Xi, Yi, Vi ≤ 1000000000

Solution

首先我们把每个圆看做一个点,将包含这个圆的最小圆看做它的父亲,这样构成一棵树,接下来就是树上距离不超过K的点对权值差的最大值,随便DP一下即可。

我们考虑如何求包含某个圆的最小圆。
用扫描线从左到右扫,用一个set维护每个圆与扫描线的交点之间的位置关系。

由于圆与圆之间不会相交,也就意味着交点 之间的相对顺序是不会改变的。
我们在加入一个圆的两个交点时,考虑比上交点高的第一个点。

如果这个点是一个上交点,那么新加的圆的父亲就是那一个点对应的圆。
否则新加的圆的父亲与那一个点对应的圆的父亲相同。

做到一个圆的最右端时就将两个交点删除。
这样就在 O ( T ( n log ⁡ n + n k ) ) O(T(n\log n+nk)) O(T(nlogn+nk))的复杂度做完了。

显然后面nk的部分可以优化,大可以采用点分治/长链剖分等做法。
毒瘤的是原题卡空间

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 100005
#define LL long long
using namespace std;
priority_queue<int,vector<int>,greater<int> > hp;
int t,fs[N],nt[N],dt[N],a[N][4],now,fa[N],m1,n,m,mx[20005][101],mi[20005][101],ans,rt[N],n1,mx1;
struct node
{
	int w,x,p;
}d[N];
bool cmp(node x,node y)
{
	return x.x<y.x;
}
LL sqr(LL x)
{
	return x*x;
}
struct nd
{
	int w,p;
	friend double wz(nd x)
	{
		if(!x.p) return a[x.w][1]-sqrt(sqr(a[x.w][2])-sqr(a[x.w][0]-now));
		else return a[x.w][1]+sqrt((sqr(a[x.w][2])-sqr(a[x.w][0]-now))); 
	}
	friend bool operator<(nd x,nd y)
	{
		return (wz(x)<wz(y));	
	}
};
set<nd> h;
void link(int x,int y)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
}
void dfs(int k,int fa)
{
	rt[k]=0;
	for(int i=fs[k];i;i=nt[i])
	{
		int p=dt[i];
		if(p!=fa)
		{
			dfs(p,k);
			if(!rt[k])
			{
				rt[k]=rt[p];
				fod(j,m,1) 
				{
					mx[rt[k]][j]=mx[rt[k]][j-1],mi[rt[k]][j]=mi[rt[k]][j-1];
					if(k) mx[rt[k]][j]=max(mx[rt[k]][j],a[k][3]),mi[rt[k]][j]=min(mi[rt[k]][j],a[k][3]);
					ans=max(ans,mx[rt[k]][j]-mi[rt[k]][j]);
				}
				if(k) mx[rt[k]][0]=mi[rt[k]][0]=a[k][3];
				else mx[rt[k]][0]=-1e9,mi[rt[k]][0]=1e9;
			}
			else
			{
				fo(j,0,m) 
				{
					if(j!=m) ans=max(ans,max(mx[rt[k]][j]-mi[rt[p]][m-j-1],mx[rt[p]][m-j-1]-mi[rt[k]][j]));
					if(j) mx[rt[k]][j]=max(mx[rt[k]][j],mx[rt[p]][j-1]),mi[rt[k]][j]=min(mi[rt[k]][j],mi[rt[p]][j-1]); 
				}
				hp.push(rt[p]);
			}
		}
	}
	if(!rt[k]) 
	{
		rt[k]=hp.top(),hp.pop();
		fo(j,0,m) mx[rt[k]][j]=mi[rt[k]][j]=a[k][3];
	}
}
int main()
{
	cin>>t;
	int cnt=0;
	while(t--)
	{
		int l=0;
		m1=0;
		while(!hp.empty()) hp.pop();
		memset(fs,0,sizeof(fs));
		scanf("%d%d",&n,&m);
		fo(i,1,n) hp.push(i);
		a[0][0]=a[0][1]=0,a[0][2]=2e9;
		fo(i,1,n) 
		{
			scanf("%d%d%d%d",&a[i][0],&a[i][1],&a[i][2],&a[i][3]);
			d[++l]=(node){i,a[i][0]-a[i][2],0},d[++l]=(node){i,a[i][0]+a[i][2],1};
		}
		sort(d+1,d+l+1,cmp);
		h.clear();
		h.insert((nd){0,0}),h.insert((nd){0,1});
		ans=0;
		fo(i,1,l)
		{
			now=d[i].x;
			if(!d[i].p)
			{
				set<nd>::iterator it=h.upper_bound((nd){d[i].w,1});
				nd p1=*it;
				if(p1.p==0) fa[d[i].w]=fa[p1.w];
				else fa[d[i].w]=(*it).w;
				link(fa[d[i].w],d[i].w);
				now=d[i].x+1;
				h.insert((nd){d[i].w,0}),h.insert((nd){d[i].w,1});
				now=d[i].x;
			}
			else h.erase((nd){d[i].w,0}),h.erase((nd){d[i].w,1});
		}
		dfs(0,0);
		cnt++;
		printf("Case %d: %d\n",cnt,ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值