[题目名称] 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;
}