思路
它这里给我们了一些扇形,叫我们求大于 k k k 层的面积(实际上是 π ( a 1 − a 2 + 1 ) r 2 2 m × 2 m π \frac{\pi(a_1-a_2+1)r^2}{2m}\times\frac{2m}{\pi} 2mπ(a1−a2+1)r2×π2m),我们把它给的这坨东西化简一下就是 ( a 1 − a 2 + 1 ) × r 2 (a_1-a_2+1)\times r^2 (a1−a2+1)×r2,现在式子化简好了,我们来考虑一下比较难处理的圆圈这个问题,大家都知道,一般一个问题在圆圈上一般都是很难处理的,但是这里我们可以算出每一个格子的值再加起来,所以可以把他当作一个直线来处理,具体怎么处理我们在来看 (不知道怎么用数组存储看下图)。
方法
我们可以考虑一下扫描线,我们把每个扇形的 a 1 a_1 a1 当作入边存储,把 a 2 a_2 a2 当作出边存储,当我们遍历到入边时就在你所用的数据结构中加一,表示这个扇形在这时存在;当我们遍历到出边时就在你所用的数据结构中减一,表示这个扇形现在不在了。在每一个格子时,我们要找到所拥有扇形中的第 k k k 大,找这个数有很多种方法,我最倾向于用树状数组加倍增,关于树状数组加倍增,先看代码吧。
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define low (x&(-x))
ll const N=2e6+5;
ll n,m,k,tree[N],a,b,r,t,ans,pp;
vector<ll> p[N];
void update(ll x,ll d){
while(x<=2e6+4)
tree[x]+=d,
x+=low;
return;
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++){//输入并把圆圈化为直线数组
cin>>r>>a>>b;
if(a>b) p[0].push_back(r);
p[a+m].push_back(r);
p[b+m].push_back(-r);
}
for(int i=0;i<2*m;i++){
for(int j=0;j<p[i].size();j++) update(abs(p[i][j]),p[i][j]/abs(p[i][j])),pp+=p[i][j]/abs(p[i][j]);
ll u=0,s=0;
if(pp>=k){//树状数组加倍增主要代码
for(int l=21;l>=0;l--)
if(u+(1<<l)<N&&s+tree[u+(1<<l)]<pp-k+1) s+=tree[u+=(1<<l)];
ans+=(u+1)*(u+1);
}
}
cout<<ans<<endl;
return 0;
}
细节
但相信大家对一些细节还有疑问,比如说如果 a 1 a_1 a1 大于 a 2 a_2 a2 怎么办,在这个时候,我们不能单纯交换他们,我们要在第一个位置再放一条边,这样子就把这个扇形分成了两个部分,在后面就不用另外考虑了。还有为什么我们要把把倍增 s s s 的边界设为 p p − k + 1 pp-k+1 pp−k+1,这很简单,就是因为我们要求的并不是第 k k k 小的而是第 k k k 大的。