题意
求出最小的 $x+y$ 之和,使得 $\forall a_i \times (t_c - x) + b_i \times (t_m - y) \le c_i$。
题解思路
显然,存在两个自变量 $x$ 和 $y$,我们函数图形应该是在一个三维空间上的一个曲面,求最小值显然很困难。
我们可以考虑令 $p = x + y$,则 $y = p - x$,此时带入:
$$a_i \times (t_c - x) + b_i \times (t_m - p + x) \le c_i$$
展开得到:
$$(b_i - a_i) \times x \le c_i - a_i \times t_c - b_i \times t_m + b_i \times p $$
因此,我们实际上可以把问题转化成:对于一个 $p$,能否找到一个合法的 $x$ 取值使得等式成立。
因此我们转而通过上述不等式来验证 $x$ 的值域是否合法:
钦定:$T = c_i - a_i \times t_c - b_i \times t_m + b_i \times p$
$$\left\{ \begin{aligned} x \le \frac{T}{b_i - a_i} (b_i - a_i > 0) \\ x \ge \frac{T}{b_i - a_i} (b_i - a_i < 0) \end{aligned} \right. $$
特别的,当 $b_i = a_i$ 时,若 $T < 0$,同样无解。
因此就变成 农夫约翰真的种地 这样维护 $x$ 值域即可。
这样,对 $p$ 二分答案即可。
tips:同样的,由于本题存在负数,向上向下取整应该使用
floor
和ceil
函数
AC CODE
#include <bits/stdc++.h>
using namespace std;
#define IOS \
ios_base::sync_with_stdio(0); \
cin.tie(0); \
cout.tie(0);
#define endl '\n'
using i64 = long long;
constexpr int N = 2e5 + 10;
void solve()
{
i64 n, tc, tm;
cin >> n >> tc >> tm;
vector<i64> a(n + 1), b(n + 1), c(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i] >> b[i] >> c[i];
auto check = [&](i64 x) -> int
{
i64 l = max(0LL, x - tm + 1);
i64 r = min(tc - 1, x);
for (int i = 1; i <= n; i++)
{
i64 k = c[i] - a[i] * tc - b[i] * tm + b[i] * x;
if (a[i] == b[i])
{
if (k < 0) return 0;
}
else if (b[i] - a[i] > 0)
r = min(r, (i64)floor(k * 1.0 / (b[i] - a[i])));
else
l = max(l, (i64)ceil(k * 1.0 / (b[i] - a[i])));
}
return l <= r;
};
i64 l = 0, r = tc + tm - 2;
while (l < r)
{
i64 mid = (l + r) >> 1;
if (check(mid))
r = mid;
else
l = mid + 1;
}
cout << r << endl;
}
int main()
{
IOS;
int _ = 1;
cin >> _;
while (_--)
solve();
return 0;
}
谢谢佬佬 刚刚一直调试不出来 现在学到了
python版
为什么用floor和不用floor结果不一样,不是会自动去掉小数部分的吗
因为不用floor是向0取整,当结果为负数的时候就会向上取整了
ok,感谢
大佬的小技巧针不辍,上次学的k*1.0这次就用上了,喜
这有什么问题吗?t=100那个样例显示答案错误#include[HTML_REMOVED]
using namespace std;
using ll=long long;
const int N=110;
ll ans;
int t;
ll a[N];
ll b[N];
ll c[N];
ll mm;
ll cc;
int n;
int check(ll p)
{
ll l=max(0ll,p-mm+1);
ll r=min(p,cc-1);
for(int i=1;i<=n;i)
{
ll T=c[i]-b[i]mm-a[i]cc+b[i]p;
if(a[i]==b[i])
{
if(T<0)
return 0;
}
else if(b[i]>a[i])
{
r=min(r,T/(b[i]-a[i]));
}
else
{
l=max(l,(ll)floor((1.0T/(b[i]-a[i])))+1);
}
}
return l<=r;
}
void slove()
{
cin>>n>>cc>>mm;
for(int i=1;i<=n;i)
cin>>a[i]>>b[i]>>c[i];
ll l=0;
ll r=mm+cc-2;
while(l[HTML_REMOVED]>1;
if(check(mid))
{
r=mid;
}
else
{
l=mid+1;
}
}
cout<[HTML_REMOVED]>t;
while(t–)
{
slove();
}
return 0;
}
为什么能用二分,单调性在哪里
因为操作次数越多,肯定能够满足条件,只有操作次数少于一定值,才会出现不满足条件,且低于这个阈值之后都无法满足
这样啊 ,谢谢大佬 。大佬,你这种数学能力是怎么练出来的,我抄你代码都抄错很多次
就是多写题,练出来的
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
#include[HTML_REMOVED]
using namespace std;
typedef long long ll;
const int N=110;
int t;
struct ct
{
ll a,b,c;
}cts[N];
ll n,tc,tm;
bool check(ll cnt)
{
ll l1=max((ll)0,cnt-tm+1);
ll r1=min(cnt,tc-1);
for(int i=1;i<=n;i++)
{
ll a=cts[i].a;
ll b=cts[i].b;
ll c=cts[i].c;
ll k=c+bcnt-btm-atc;
if(a-b==0)
{
if(k<0) return false;
}
else if(a<b)
{
r1=min(r1,ll(ceil(k1.0/(b-a)))-1);
}
else{
l1=max(l1,ll(floor(k*1.0/(b-a)))+1);
}
}
return l1<=r1;
}
int main()
{
cin>>t;
while(t–)
{
cin>>n>>tc>>tm;
for(int i=1;i<=n;i++)
{
cin>>cts[i].a>>cts[i].b>>cts[i].c;
}
ll l=-1,r=tc+tm;
while(l+1!=r)
{
ll mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid;
}
cout<<r<<endl;
}有没有大佬解释一下这样写为什么过不去qwq
不懂就问,check里面一开始的l和r代表的是什么啊
x的值域
orz
终于过了,但是还是感觉有点模糊。
烤箱制作某个东西的时间为什么不能降到0。
debug的时候还被卡了右边界,就是如果一开始二分的时候r设置很大会直接寄T_T
orz
一开始做题的时候发现只需要把所有方程解一下最后求一个交集就能有,看着感觉可以高斯消元,但是又不会写。实际上能消元吗,应该不能用于不等式组吧,
不可以用gauss消元
好的,感谢大佬指点orz
请教一下,我自己写的代码里面不能用tm这个词,说是ambiguous,好像是关键字,但是我直接复制你的代码,你的tm不会报错
因为我的tm是局部变量
嗷嗷,好的,谢谢