【JZOJ B组】【NOIP2013模拟】小喵喵的新家

Description

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

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

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

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

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

Input

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

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

Output

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

Sample Input

3 8 2

1 -8 8

3 -7 3

5 -5 5

Sample Output

76

Data Constraint

SOB

Hint

这里写图片描述

思路

对于100%的做法:
扇形的面积:(所占的份数/2m)*πr^2
题目说:答案要乘一个2m/π
这样一相乘 化简:所占份数*r^2
就不用考虑精度问题啦!!!(出题人好评)
现在就要求出所有被覆盖大于等于k的面积,半径就是第k大的半径
那一个部分中有什么半径,我们怎么知道呢?
我们把圆拆成一条线段,端点就是圆圈上的各个等分点
把地毯的半径视为高,连接起始点和终点,如果有横跨线段中点的,把它看作两个部分
样例如下图:(用denghan大爷一张图)

这里写图片描述

设g[r]为半径为r的数量
我们可以从-m扫过去,碰到起始点的时候就把其对应的g[r]+1,碰到结束点的时候就把其对应的g[r]-1(也就类似与差分约束)

现在就要求第k大值

用线段树维护就好了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M=100000,maxn=M*5+77;
long long ans;
int n,m,k,cnt,p[maxn],to[maxn],head[maxn],w[maxn];
struct E
{
    int a,b,c;
}e[maxn];
void add(int x,int y,int r) 
{
    e[++cnt].a=x; e[cnt].b=y; e[cnt].c=r;
}
int get_val(int x)
{
    return x<0?-x+M:x;
}
void insert(int l,int r,int d,int a,int b)
{
    if(l==r)
    {
        p[d]+=b; return;
    }
    int mid=(l+r)/2;
    if (a<=mid) insert(l,mid,d*2,a,b); else insert(mid+1,r,d*2+1,a,b);
    p[d]=p[d*2]+p[d*2+1];
}
int find(int l,int r,int d,int k)
{
    if(l==r) return p[d]>=k?l:0;
    int mid=(l+r)>>1;
    if(p[d*2+1]>=k) return find(mid+1,r,d*2+1,k);else find(l,mid,d*2,k-p[d*2+1]);
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1; i<=n; i++)
    {
        int r,s,t;
        scanf("%d%d%d",&r,&s,&t);
        if(s==-m||s==m) add(-m,1,r),add(t,-1,r);
        else
        {
            if(t==-m) t=m;
            if(t>=s) add(s,1,r),add(t,-1,r);
            else add(s,1,r),add(m,-1,r),add(-m,1,r),add(t,-1,r);
        }
    }
    for(int i=1; i<=cnt; i++)
    {
        int j=get_val(e[i].a);
        if (!head[j]) head[j]=i; else to[w[j]]=i;
        w[j]=i;
    }
    for(int i=-m; i<=m-1; i++)
    {
        for(int j=head[get_val(i)]; j; j=to[j]) insert(1,M,1,e[j].c,e[j].b);
        long long t=find(1,M,1,k);
        ans+=t*t;
    }
    printf("%lld",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值