题解 点点的圈圈
发现对于每个圆,从能包含它的最小圆向它建边后可以简单 DP
于是考虑优化建边
有一个乱搞思路是树套树
查询能包含当前圆的最小正方形中的所有小圆,再逐一判断
确实可以被同心圆+圆周上均匀分布卡到 \(O(n^2)\) 级别
然后考虑[优/劣]化,发现可以用 2D-tree 查询与圆心距离 \(\leqslant r\) 的点
这样在随机数据下好像更快一点
最后正解:
是利用了因为所有圆无交,所以在扫描线上所有半圆的位置关系不变
这样复杂度是 \(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;
}