题解
先考虑答案是什么,在每一个块
显然是每个块,最大的被覆盖了k次的半径的平方的和。
其实圆至少为了求答案二出的一个背景,
想像一下,将这个圆沿着某个半径切开,拉开成一个一个矩形。
那么一个覆盖操作就变成了一个矩形,
而现在也就是要求每一列,覆盖能被k次的最高高度。
用扫描线,树状数组维护每个位置被覆盖的次数。
因为每个位置覆盖次数都是从下往上是递减的,
于是可以二分。
具体做法,
将枚举矩形拆成两个操作,左边界加,右边界减。
然后对所有操作排序。
从左往右,枚举每一行,
用树状数组的前缀和来表示这个位置被覆盖了多少次,
二分一个答案,在树状数组里面查询。
所以总的时间复杂度是
O
(
n
l
o
g
n
2
)
O(nlog_n^2)
O(nlogn2)
要注意跨过分割半径的去拆成两个区间。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
char ch;
void read(int&n)
{
int w=1;
for(ch=getchar();(ch<'0' || ch>'9') && ch!='-';ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(n=0;'0'<=ch && ch<='9';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
n=n*w;
}
const int N=100003;
int n,m,k,l,r,mid,mxr;
int x,y,z,s[N],tot,id,t;
ll ans;
struct node
{
int x,k,v;
}a[3*N];
bool cmp(node a,node b){return a.x<b.x;}
int x_(int x){return x&(-x);}
void ins(int x){for(int i=x;i<=mxr;i=i+x_(i))s[i]++;}
void ins_(int x){for(int i=x;i<=mxr;i=i+x_(i))s[i]--;}
int find(int x){int S=0;for(int i=x;i;i=i-x_(i))S=S+s[i];return S;}
int main()
{
freopen("xiaoqiao.in","r",stdin);
freopen("xiaoqiao.out","w",stdout);
read(n);read(m);read(k);
for(int i=1;i<=n;i++)
{
read(x);read(y);read(z);
x++;mxr=(mxr>x?mxr:x);
if(y<z)
{
tot++;a[tot].x=y,a[tot].k=x;a[tot].v=1;
tot++;a[tot].x=z,a[tot].k=x;a[tot].v=-1;
}
else
{
tot++;a[tot].x=-m,a[tot].k=x;a[tot].v=1;
tot++;a[tot].x=z,a[tot].k=x;a[tot].v=-1;
tot++;a[tot].x=y,a[tot].k=x;a[tot].v=1;
}
}
sort(a+1,a+1+tot,cmp);id=1;
for(int i=-m;i<m;i++)
{
for(;id<=tot && a[id].x==i;id++)
if(a[id].v==1)ins(1),ins_(a[id].k);else ins_(1),ins(a[id].k);
t=0;
for(l=0,r=mxr;l<r;)
{
mid=(l+r)>>1;
if(find(mid)>=k)l=mid+1,t=mid;else r=mid;
}
if(find(l)>=k)t=max(t,l);
ans=ans+(ll)t*t;
}
printf("%lld",ans);
}