4.9 省选模拟赛 圆圈游戏 树形dp set优化建图

avatar
avatar

由于圆不存在相交的关系 所以包容关系形成了树的形态 其实是一个森林 不过加一个0点 就变成了树。

考虑对于每个圆都求出最近的包容它的点 即他的父亲。然后树形dp即可。暴力建图n^2.

const int MAXN=100010;
int n,m,len;
struct wy
{
	ll x,y,r,w;
	inline int friend operator <(wy a,wy b){return a.r<b.r;}
}t[MAXN];
int f[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
inline db dist(int x,int y){return sqrt((pf(t[x].x-t[y].x)+pf(t[x].y-t[y].y))*1.0);}
inline void dp(int x)
{
	f[x]=t[x].w;
	int sum=0;
	go(x)
	{
		dp(tn);
		sum+=f[tn];
	}
	f[x]=max(f[x],sum);
}
inline void add(int x,int y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
int main()
{
	freopen("circle.in","r",stdin);
	freopen("circle.out","w",stdout);
	get(n);
	rep(1,n,i)
	{
		ll x,y,r,w;
		get(x);get(y);
		get(r);get(w);
		t[i]=(wy){x,y,r,w};
	}
	sort(t+1,t+1+n);
	rep(1,n,i)//对于每个i找到一个最小的j.
	{
		db minn=INF;int p=0;
		rep(i+1,n,j)
		{
			db d=dist(i,j);
			if(t[j].r-t[i].r-d<0)continue;
			if(t[j].r-t[i].r-d<minn)
			{
				minn=t[j].r-t[i].r-d;
				p=j;
			}
		}
		add(p,i);
	}
	dp(0);put(f[0]);return 0;
}

考虑优化建图 一个思路 把所有的边都连上 然后topsort建图 但是这并不能线段树优化建图什么的。

或者直接对于每个圆找到离自己最近的圆然后判断关系连边。

对于后者 可以考虑以扫描线的方式建图 对于每个圆我们都在左边插入 右边删除。

在插入的时候寻找父亲 可以发现此时圆对于离自己最近的圆要么是包含的 要么是兄弟。

对于前者直接找到了父亲 对于后者 兄弟的父亲就是自己的父亲。

考虑找到最近的圆可以使用圆的上半部分来判断 对于上半部分找到自己左端点离自己最近的圆弧 如果是下半圆弧就是兄弟 上半圆弧那么必然是父亲。

用set维护距离 可以发现这些圆弧的相对位置不变 所以总复杂度nlogn.

const ll MAXN=200010,maxn=3000010;
ll n,T,cnt,len;
ll f[MAXN];
ll lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
struct wy{ll x,y,z,r,id;}t[MAXN];
struct jl{ll x,y,op;}q[MAXN];
inline ll cmp(jl a,jl b){return a.x<b.x;}
inline void dp(ll x)
{
	f[x]=t[x].z;
	ll sum=0;
	go(x)
	{
		dp(tn);
		sum+=f[tn];
	}
	f[x]=max(f[x],sum);
}
inline void add(ll x,ll y)
{
	ver[++len]=y;
	nex[len]=lin[x];
	lin[x]=len;
}
struct data
{
	ll id,op;
	//data(){};
	inline double calc()const
	{
		if(op)return t[id].y+sqrt((pf(t[id].r)-pf(t[id].x-T))*1.0);
		return t[id].y-sqrt((pf(t[id].r)-pf(t[id].x-T))*1.0);
	}
	inline ll friend operator <(data a,data b)
	{
		double x=a.calc();db y=b.calc();
		if(fabs(y-x)>EPS)return x<y;
		if(a.op!=b.op)return a.op<b.op;
		return a.id<b.id;
	}
};
set<data>s;
set<data>::iterator it;
signed main()
{
	freopen("circle.in","r",stdin);
	freopen("circle.out","w",stdout);
	get(n);
	rep(1,n,i)
	{
		ll x,y,z,r;
		get(x);get(y);get(r);get(z);
		t[i]=(wy){x,y,z,r};
		q[++cnt]=(jl){t[i].x-t[i].r,i,1};
		q[++cnt]=(jl){t[i].x+t[i].r,i,-1};
	}
	sort(q+1,q+1+cnt,cmp);
	//rep(1,cnt,i)cout<<q[i].y<<endl;
	rep(1,cnt,i)
	{
		T=q[i].x;
		//cout<<(*s.begin()).id<<' '<<(*s.begin()).op<<endl;
		if(q[i].op==1)
		{
			s.insert((data){q[i].y,1});
			it=s.find((data){q[i].y,1});
			++it;
			if(it==s.end())f[q[i].y]=0;
			else
			{
				if((*it).op==0)f[q[i].y]=f[(*it).id];
				else f[q[i].y]=(*it).id;
			}
			s.insert((data){q[i].y,0});
		}
		else
		{
			s.erase((data){q[i].y,1});
			s.erase((data){q[i].y,0});
		}
	}
	rep(1,n,i)add(f[i],i);
	dp(0);put(f[0]);return 0;
}

一些细节:两个圆并列的时候注意让下半圆弧优先 注意距离的计算公式。

原文地址:https://www.cnblogs.com/chdy/p/12681119.html