DTOJ #2768. 圆圈游戏(circle)

【题目描述】

在无聊的时候,小 $K$ 和小 $H$ 会在纸上玩这样一个游戏。

我们可以将纸看做一个平面直角坐标系。小 $H$ 会先在上面画出 $n$ 个圆,并把每个圆的圆心以及半径都告诉小 $K$。小 $H$ 画的 $n$ 个圆中,任意两个圆不会出现相交或相切的情况。小 $K$ 需要做的就是从这 $n$ 个圆中选出若干个圆,使得选出的任意一个圆都不被另一个选出的圆包含。游戏的目标就是要选出尽量多的圆。

游戏一次一次进行着,小 $K$ 已经对游戏的规则感到了厌倦,所以他决定修改游戏的规则。对于第 $i$ 个圆,我们定义它的价值为 $w_i$ 。新的游戏目标是使得选出的圆价值和最大(不一定数量最多)。但是圆圈可能很多,或者圆圈的分布非常奇怪,或者小 $K$ 还有别的事情要做。所以他只好拜托你来帮他求出这个最大值了。

【输入格式】

第一行一个整数 $n$ 表示圆圈的个数。

接下来 $n$ 行每行 $4$ 个整数 $x_i,y_i,r_i$ 和 $w_i$ ,分别表示第 $i$ 个圆的圆心横坐标、纵坐标、半径,和价值。

【输出格式】

输出一行,包含一个整数,代表选出的圆的最大价值。

【样例】

样例输入
3
3 4 2 3
6 4 7 5
9 4 1 4


样例输出
7

样例解释

如果选择价值最大的圆 $2$ ,可以获得的价值和为 $5$。如果选择圆 $1$ 和圆 $3$,虽然它们的单个价值都不是最大的,但价值和可以达到 $3+ 4 = 7$。

【数据范围与提示】

测试点编号 $n=$
$1$ $1$
$2$ $2$
$3$ $3$
$4$ $4$
$5$ $8$
$6$ $12$
$7$ $16$
$8,9$ $1000$
$10,11$ $2000$
$12,13$ $3000$
$14,15$ $5000$
$16,17$ $60000$
$18,19$ $70000$
$20,21$ $80000$
$22,23$ $90000$
$24,25$ $100000$

 

对于全部测试数据 $1 \le x_i,y_i,r_i \le 10^8,1 \le w_i \le 1000 $。

保证不存在相交或相切的两个圆。

【题解】

看到不互相包含的圆,显然是最大权独立集问题。又注意到每个圆一定被另一个最小圆包含,因此圆与圆之间由包含关系构成树形结构。若能够建出树则可以树形 $dp$。

考虑 $60$ 分做法。按 $r$ 从小到大扫一遍,对于每个圆找到最小的包含它的圆,直接建边跑树形 $dp$ 即可。效率 $O(n^2)$。

考虑如何优化建树。首先可以反过来建边,从外往里。同样从小到大插入每个圆。由于圆与圆不相交,圆心在当前插入圆内的圆一定被当前圆包含。直接连边然后删除即可。

由于是二维限制,用 $kd-tree$ 或二维线段树均可。

另一种做法是用扫描线,对于每个圆拆成上下两半,左右两个端点视为插入与删除的扫描线。用 $set,splay$ 等数据结构维护每个半圆的位置上下关系。查询时查询上半圆前驱与下半圆后继即可。

【代码】

#include<bits/stdc++.h>
inline int read ( void )
{
	int x=0;char ch;bool f=true;
	while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false;
	for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
	return f ? x : -x ;
}
int f[100010],n,tot,root;
struct tree { int ch[4],tot,id; } t[10000010];
struct Circle { long long x,y,r;int w; } C[100010];
std::vector<int> E[100010];
const int inf=1000000000;
const int L=-inf,R=inf;
inline bool inside ( int l,int r,int d,int u,int x,int y,long long R )
{
	long long dx=std::max(std::max(x-r,l-x),0),dy=std::max(std::max(y-u,d-y),0);
	return (dx*dx+dy*dy<=R*R);
}
inline void pushup ( int k ) { t[k].tot=t[t[k].ch[0]].tot+t[t[k].ch[1]].tot+t[t[k].ch[2]].tot+t[t[k].ch[3]].tot; }
inline int query ( int l,int r,int d,int u,int k,int x,int y,int R )
{
	if ( !t[k].tot or !inside(l,r,d,u,x,y,R) ) return 0;
	if ( l==r and d==u ) { t[k].tot=0;return f[t[k].id]; }
	int mid1=(l+r)>>1,mid2=(d+u)>>1,res=0;
	res+=query(l,mid1,d,mid2,t[k].ch[0],x,y,R);
	res+=query(l,mid1,mid2+1,u,t[k].ch[1],x,y,R);
	res+=query(mid1+1,r,d,mid2,t[k].ch[2],x,y,R);
	res+=query(mid1+1,r,mid2+1,u,t[k].ch[3],x,y,R);
	pushup(k);
	return res;
}
inline void modify ( int l,int r,int d,int u,int &k,int x,int y,int i )
{
	if ( !k ) k=++tot;
	if ( l==r and d==u ) { t[k].tot=1;t[k].id=i;return; }
	int mid1=(l+r)>>1,mid2=(d+u)>>1;
	if ( x<=mid1 and y<=mid2 ) modify(l,mid1,d,mid2,t[k].ch[0],x,y,i);
	if ( x<=mid1 and y>mid2 ) modify(l,mid1,mid2+1,u,t[k].ch[1],x,y,i);
	if ( x>mid1 and y<=mid2 ) modify(mid1+1,r,d,mid2,t[k].ch[2],x,y,i);
	if ( x>mid1 and y>mid2 ) modify(mid1+1,r,mid2+1,u,t[k].ch[3],x,y,i);
	pushup(k);
}
signed main()
{
	n=read();
	for ( int i=1;i<=n;i++ ) C[i].x=read(),C[i].y=read(),C[i].r=read(),C[i].w=read();
	++n;C[n].r=inf;std::sort(C+1,C+n+1,[&](const Circle &c1,const Circle &c2){return c1.r<c2.r;});
	for ( int i=1;i<=n;i++ ) f[i]=std::max(query(L,R,L,R,root,C[i].x,C[i].y,C[i].r),C[i].w),modify(L,R,L,R,root,C[i].x,C[i].y,i);
	return !printf("%d\n",f[n]);
}

  

 

posted @ 2019-08-06 21:01  DTOI_RSY  阅读(428)  评论(0编辑  收藏  举报