小喵喵的新家

JZOJ3441 小喵喵的新家

Description

小喵喵和小聪聪从小就是好朋友 ,他们经常在一起玩耍 。如今小喵已经厌倦了自己居住的环境,想请小聪聪为她建一个新家。

小喵喵天生多才多艺,对多种乐器颇有研究。对于生活中常见的图形,她对圆形很感兴趣,因此小聪聪决定为她建一个圆形的新家。

我们设新家在一个平面直角坐标系上,其中新家的圆心为平面直角坐标系的原点。

小聪聪有一把神奇的剪刀,他定义了一个值m,以等分 [−pi,pi]弧度 (详见样例)。他还有一支神奇的画笔,将进行 n次“铺地毯”操作。对于第i 次“铺地毯”操作,他将设定一个半径ri,起始位置si,终止位置ti ,然后从圆心角pi*si/m到圆心角pi*ti/m这部分区域逆时针铺上一个扇形地毯。

小喵喵想到了一个奇怪的问题,她想知道有多大面积被至少铺过k次地毯。 这个问题一下就难倒了聪明的小聪聪。 现在小聪聪求助于你,你能帮他解决这个问题吗?为了方便表达 ,设答案的值为T,你只需要输出 T×2m/ π 的值即可 。

Input

第一行是三个整数 n,m,k,含义 如题目描述中所述。

接下来n行, 每行描述一次铺地毯操作 。第i行有三个整数r,si,ti,含义 如 题目描述中所述。

Output

输出 一个整数 表示T×2m/ π 的值。

Sample Input

3 8 2

1 -8 8

3 -7 3

5 -5 5

Sample Output

76

Data Constraint

这里写图片描述

Hint

这里写图片描述

分析:

出题人用心良苦,不卡精度。

实际上算面积的时候很简单。

本来在题中扇形面积的计算公式是这样的:

S=2mπr2

由于“你只需要输出 T×2m/π的值即可 ”,所以就变成了:

S=r2

把整幅图看作一个半径为100000的大圆。

它被分成了2m份,我们一份一份求覆盖不小于k次的面积。

这里写图片描述

如图所标,我们红色的扇形被三条毛毯所穿过,题目说被不少于k条毛毯覆盖,那我们就从半径最大的那条毛毯往内数,数够k条后,你会发现当前那条第k长的半径所围起来的地方就是该部分被不少于k条毛毯所覆盖的地方。

这里写图片描述

当我们知道一部分中有哪些半径时,我们通过二分,就能求出答案。

那一个部分中有什么半径,我们怎么知道呢?

我们把圆拆成一条线段,端点就是圆圈上的各个等分点。把地毯的半径视为高,连接起始点和终点,如果有横跨线段中点的,把它看作两个部分。然后把起点和终点分别存出来,附带半径长,按照端点排序(边集数组会更快)。

样例如下图:

这里写图片描述

设g[r]表示现在半径为r的半径有多少条。

我们可以从-m扫过去,碰到起始点的时候就把其对应的g[r]+1,碰到结束点的时候就把其对应的g[r]-1。记得用线段树维护1-100000内g[r]的和。

之后利用线段树的基本性质二分:

现在有l,r,k1。
mid=(l+r)/2;

我们要在l,r中找一个最大的a,使得 ri=ag[i] =k1。

如果 ri=mid+1g[i] >=k1,则说明a一定在mid+1—r中,递归(mid+1,r,k1)。否则,a要么在l—mid中,要么不存在,递归(l,mid,k1- ri=mid+1g[i] )。

之所以要减去 ri=mid+1g[i] ,是因为我们要使 ri=ag[i] =k1,而 ri=mid+1g[i] 是包括在内的,所以递归下去也要算,就直接把它减去。

找到a以后,答案加上 a2 ,注意开long long(int 64)。

Code:

#include<cstdio>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;
int n,m,k,r,s,t,tot,tail,d[500000],x[500000],l[200001],w[200001];;
long long ans;
int v(int x){
    if(x<0) return -x+100000; else return x;
}
struct cc{
    int a,b,c;
}b[500001];
void change(int x,int y,int i,int a,int c){
    if(x==y){
        d[i]+=c;
        return;
    }
    int m=(x+y)/2;
    if(a<=m) change(x,m,i+i,a,c); else change(m+1,y,i+i+1,a,c);
    d[i]=d[i+i]+d[i+i+1];
}
int find(int x,int y,int i,int k){
    if(x==y) return d[i]>=k?x:0;
    int m=(x+y)/2;
    return d[i+i+1]>=k?find(m+1,y,i+i+1,k):find(x,m,i+i,k-d[i+i+1]);
}
void insert(int x,int y){
    b[++tot].a=x; b[tot].b=y; b[tot].c=r;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    fo(i,1,n){
        scanf("%d%d%d",&r,&s,&t);
        if(s==-m||s==m){
            insert(-m,1); insert(t,-1);
        } else {
            if(t==-m) t=m;
            if(t>=s){
                insert(s,1); insert(t,-1);
            } else {
                insert(s,1); insert(m,-1);
                insert(-m,1); insert(t,-1);
            }
        }
    }
    fo(i,1,tot){
        int j=v(b[i].a);
        if(!l[j]) l[j]=i; else x[w[j]]=i;
        w[j]=i;
    }
    fo(i,-m,m-1){
        for(int j=l[v(i)];j;j=x[j])
            change(1,100000,1,b[j].c,b[j].b);
        long long p=find(1,100000,1,k);
        ans+=p*p;
    }
    printf("%lld",ans);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值