(单调栈)洛谷 P6875 COCI 2013/2014 KRUŽNICE 题解
题意
现在有 NNN 个以 xxx 轴为中心的互不相交的圆,但圆周可以接触。请问这些圆把平面分成多少块?
1≤N≤3×105,−109≤xi≤1091 \le N \le 3\times 10^5,-10^9 \leq x_i \leq 10^91≤N≤3×105,−109≤xi≤109,1≤ri≤1091 \leq r_i \leq 10^91≤ri≤109。
思路
除了外面一片空白,每个圆内部都会贡献 111,但是当:
的时候,形如若干个圆覆盖了某个更大圆的直径时,就会多 111 的贡献。(题目保证不会出现任意两个圆有两个交点的情况)
我们知道每个圆的中心坐标和半径,进而知道左、右端坐标。从左到右依次加圆,先加小圆(体现为按照右端点排序)。我们发现一个圆要么接着上一个圆的右端点,要么接到上若干个圆的右端点。出现第二种情况时,当前圆 ooo 内部,可能被分成两部分。
于是维护一个单调栈,维护圆的左端点单调递增。每次加圆 ooo,就把左端点 >o.left>o.left>o.left 的圆弹出——这些被弹出的圆必然在 ooo 内部。如果这些圆的直径之和恰为当前圆的直径(如上所述),那么 ooo 就被分成了两部分。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3e5+9;
ll n;
struct circ
{ll x,R;ll l,r;
}a[N];
bool cmp(circ x,circ y)
{if(x.r!=y.r)return x.r<y.r;return x.l>y.l;
}
ll stk[N],top;
int main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){ll x,r;scanf("%lld%lld",&x,&r);a[i]=(circ){x,r,x-r,x+r};}sort(a+1,a+n+1,cmp);ll ans=n+1;//背景和n个圆 for(int i=1;i<=n;i++){ll s=0;while(top&&a[stk[top]].l>=a[i].l) {s+=a[stk[top]].R*2;top--;}stk[++top]=i;if(s==a[i].R*2)ans++;//填满直径 }printf("%lld",ans);return 0;
}