题解 点点的圈圈

传送门

发现对于每个圆,从能包含它的最小圆向它建边后可以简单 DP
于是考虑优化建边
有一个乱搞思路是树套树
查询能包含当前圆的最小正方形中的所有小圆,再逐一判断
确实可以被同心圆+圆周上均匀分布卡到 \(O(n^2)\) 级别

然后考虑[优/劣]化,发现可以用 2D-tree 查询与圆心距离 \(\leqslant r\) 的点
这样在随机数据下好像更快一点

最后正解
image
image

是利用了因为所有圆无交,所以在扫描线上所有半圆的位置关系不变
这样复杂度是 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define fir first
#define sec second
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int uni[N], usiz;
int sta[N], top, ans;
struct circle{int x, y, r, w;}a[N];
inline bool operator < (circle a, circle b) {return a.r<b.r;}
inline ll sqr(ll a) {return a*a;}
inline bool isinc(const circle& a, const circle& b) {return sqr(a.x-b.x)+sqr(a.y-b.y)<=sqr(a.r+b.r);}

namespace force{
	void solve() {
		int lim=1<<n;
		for (int s=0; s<lim; ++s) {
			top=0; int sum=0;
			for (int i=0; i<n; ++i) if (s&(1<<i)) sta[++top]=i+1, sum+=a[i+1].w;
			for (int i=1; i<=top; ++i)
				for (int j=1; j<=top; ++j)
					if (i!=j && isinc(a[sta[i]], a[sta[j]]))
						goto jump;
			ans=max(ans, sum);
			jump: ;
		}
		printf("%d\n", ans);
	}
}

namespace task1{
	set<pair<int, int>> dat[N<<2];
	int dp[N], tl[N<<2], tr[N<<2];
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	#define dat(p) dat[p]
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r;
		if (l==r) return ;
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
	}
	void upd(int p, int pos, pair<int, int> val) {
		dat(p).insert(val);
		if (tl(p)==tr(p)) return ;
		int mid=(tl(p)+tr(p))>>1;
		if (pos<=mid) upd(p<<1, pos, val);
		else upd(p<<1|1, pos, val);
	}
	void del(int p, int pos, pair<int, int> val) {
		// assert(dat(p).find(val)!=dat(p).end());
		dat(p).erase(val);
		if (tl(p)==tr(p)) return ;
		int mid=(tl(p)+tr(p))>>1;
		if (pos<=mid) del(p<<1, pos, val);
		else del(p<<1|1, pos, val);
	}
	void query(int p, int xl, int xr, int yl, int yr) {
		if (xl<=tl(p)&&xr>=tr(p)) {
			auto it=dat(p).lower_bound({yl, 0});
			for (; it!=dat(p).end() && it->fir<=yr; ++it) sta[++top]=it->sec;
			return ;
		}
		int mid=(tl(p)+tr(p))>>1;
		if (xl<=mid) query(p<<1, xl, xr, yl, yr);
		if (xr>mid) query(p<<1|1, xl, xr, yl, yr);
	}
	void solve() {
		sort(a+1, a+n+1);
		build(1, 1, usiz);
		for (int i=1,l,r; i<=n; ++i) {
			// cout<<"i: "<<i<<endl;
			l=lower_bound(uni+1, uni+usiz+1, a[i].x-a[i].r)-uni;
			r=upper_bound(uni+1, uni+usiz+1, a[i].x+a[i].r)-uni-1;
			// assert(uni[l]>=a[i].x-a[i].r && uni[r]<=a[i].x+a[i].r);
			top=0; query(1, l, r, a[i].y-a[i].r, a[i].y+a[i].r);
			int sum=0;
			for (int j=1; j<=top; ++j) if (isinc(a[i], a[sta[j]])) {
				sum+=dp[sta[j]];
				del(1, lower_bound(uni+1, uni+usiz+1, a[sta[j]].x)-uni, {a[sta[j]].y, sta[j]});
			}
			dp[i]=max(a[i].w, sum);
			upd(1, lower_bound(uni+1, uni+usiz+1, a[i].x)-uni, {a[i].y, i});
		}
		top=0; query(1, 1, usiz, 1, 1e9);
		for (int i=1; i<=top; ++i) ans+=dp[sta[i]];
		printf("%d\n", ans);
	}
}

signed main()
{
	n=read();
	for (int i=1,x,y,r,w; i<=n; ++i) {
		uni[++usiz]=x=read(); y=read(); r=read(); w=read();
		a[i]={x, y, r, w};
	}
	sort(uni+1, uni+usiz+1);
	usiz=unique(uni+1, uni+usiz+1)-uni-1;
	// force::solve();
	task1::solve();

	return 0;
}
posted @ 2022-01-19 06:56  Administrator-09  阅读(4)  评论(0编辑  收藏  举报