题解
- 对于60%的做法:
- 前6个点,直接上暴力,别怂
- 对于后6个点,所有的半径都是相同的,直接用差分约束思想,在扇形开始为1,结束为-1
- 做一遍前缀和,然后将大于等于k的区间的长度求出来,算面积
- 对于100%的做法:
- 扇形的面积:(所占的份数/2m)*πr^2
- 题目说:答案要乘一个2m/π
- 这样一相乘 化简:所占份数*r^2
- 就不用考虑精度问题啦!!!(出题人好评)
- 现在就要求出所有被覆盖大于等于k的面积,半径就是第k大的半径
- 那一个部分中有什么半径,我们怎么知道呢?
- 我们把圆拆成一条线段,端点就是圆圈上的各个等分点
- 把地毯的半径视为高,连接起始点和终点,如果有横跨线段中点的,把它看作两个部分
- 样例如下图:(用denghan大爷一张图)
-
- 设g[r]为半径为r的数量
-
我们可以从-m扫过去,碰到起始点的时候就把其对应的g[r]+1,碰到结束点的时候就把其对应的g[r]-1(也就类似与差分约束)
-
现在就要求第k大值
- 用线段树维护就好了
代码
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 int n,m,k,r,s,t,cnt,p[500010],to[500010],head[200010],w[200010]; 5 long long ans; 6 struct edge { int a,b,c; }e[500010]; 7 void insert(int x,int y) { e[++cnt].a=x; e[cnt].b=y; e[cnt].c=r; } 8 int abs(int x) { return x<0?-x+100000:x; } 9 void change(int l,int r,int d,int a,int b) 10 { 11 if (l==r) 12 { 13 p[d]+=b; 14 return; 15 } 16 int mid=(l+r)/2; 17 if (a<=mid) change(l,mid,d*2,a,b); else change(mid+1,r,d*2+1,a,b); 18 p[d]=p[d*2]+p[d*2+1]; 19 } 20 int find(int l,int r,int d,int k) 21 { 22 if (l==r) return p[d]>=k?l:0; 23 int mid=(l+r)/2; 24 return p[d*2+1]>=k?find(mid+1,r,d*2+1,k):find(l,mid,d*2,k-p[d*2+1]); 25 } 26 int main() 27 { 28 scanf("%d%d%d",&n,&m,&k); 29 for (int i=1;i<=n;i++) 30 { 31 scanf("%d%d%d",&r,&s,&t); 32 if (s==-m||s==m) insert(-m,1),insert(t,-1); 33 else 34 { 35 if (t==-m) t=m; 36 if (t>=s) insert(s,1),insert(t,-1); 37 else insert(s,1),insert(m,-1),insert(-m,1),insert(t,-1); 38 } 39 } 40 for (int i=1;i<=cnt;i++) 41 { 42 int j=abs(e[i].a); 43 if (!head[j]) head[j]=i; else to[w[j]]=i; 44 w[j]=i; 45 } 46 for (int i=-m;i<=m-1;i++) 47 48 { 49 for (int j=head[abs(i)];j;j=to[j]) change(1,100000,1,e[j].c,e[j].b); 50 long long a=find(1,100000,1,k); 51 ans+=a*a; 52 } 53 printf("%lld",ans); 54 return 0; 55 }