Massacre at Béziers

[题目名称] Massacre at Béziers

[问题描述]

我妻蛤乃给你出了一道送命题:
黄梅时节家家雨,青草池塘处处蛙~
有n只青蛙,第i只青蛙会每过xi秒会连续叫yi秒。然而由 于青蛙的寿命在增加,所以从第二次开始每次休息结束后这只青蛙连续叫的时间会增加zi秒。
给定n只青蛙,每一只的xi,yi,zi,以及时间t,求在前t秒中,所有青蛙共叫了多少秒。

[输入格式]

第一行两个数n和t;
之后n行,第i+1行每行三个非负整数xi,yi,zi。

[输出格式]

一行一个数表示答案。

[样例解释]

这里写图片描述

[样例输入]

  • 样例1

    8 10
    9 1 1
    1 9 9
    4 1 0
    2 3 3
    1 0 0
    1 4 0
    9 2 5
    1 2 1

  • 样例2

    1 233333
    233 233 233

  • 样例3

    10 100000000
    1 0 0
    1 0 5

[样例输出]

  • 样例1

    34

  • 样例2

    223081

  • 样例3

    845787522

[时空上限]

时间:1秒
空间:512MB






·

很容易想到对每只青蛙分别进行处理,tj=T-tb (tj,tb,T 分别表示这只青蛙叫的总时间,不叫的总时间 和 总时间)。由于 T 是已知的,所以我们的首要任务就是求出tb。
如果这只青蛙的 zi 为 0,我们就可以直接求出 tb。
这题的麻烦之处就在于有时候 zi ≠ 0,每次叫的时间都会延长 zi 秒,如果把一次休息和一次叫看成一个整体,那么这个整体的总耗时就是(k表示这是第 k 次鸣叫)xi + yi + (k - 1)*zi。对于同一只青蛙,xi 和 yi 也是不变的,唯独 zi 影响到每次的时间。每次都比上一次延长 zi 秒,提取公因式会得到 k 次 鸣叫&&休息 的总耗时 = k*(xi + yi) + 0 + zi + zi*2 + zi*3 + zi*4 + ······ + zi*(k-1) = k*(xi + yi) + k*(k-1)/2*zi
如此一来,我们就可以通过二分来确定每只青蛙在规定时间内能经过几个“整体”过程,也就知道休息的时间,等于完整度过的“整体”数 * xi + 零头。
因为后一项有个 k*(k-1)/2 可以近似看成 k^2,所以边界到 sqrt(T/2) 就好了(这样可以避免long long/int64 乘爆)。当然如果嫌麻烦可以把乘法转化为除法,也可以避免乘法爆掉(毕竟二分是log级的)。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Q(ret) (ret<<3)+(ret<<1)
#define LL long long
#define db double
using namespace std;
const int maxn=100005;
int n,x;
LL t,ans,A,B;
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=Q(ret)+ch-'0',ch=getchar();
    return ret*f;
}
int working()
{
    if (A==x&&!B) return 0;
    int L=0,R=t;LL p;
    while (L<=R)
    {
        int mid=(R-L>>1)+L;
        p=((LL)mid*(mid-1)>>1);
        if ((db)(t-A*mid)/p>=B) L=mid+1;else R=mid-1;
    }
    p=A*R+((LL)R*(R-1)>>1)*B;
//  LL s=t-R*x-min(t-p,(LL)x);cout<<s<<'+'<<endl;
    return t-R*x-min(t-p,(LL)x);
}
int main()
{
    freopen("brute.in","r",stdin);
    freopen("brute.out","w",stdout);
    n=read();t=read();
    for (int i=1;i<=n;i++)
    {
        x=read();A=(LL)x+read();
        B=read();ans+=working();
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值