货币系统(二分)

problem

【题目描述】
你在 NOIP 2018 的赛场上遇到了「货币系统」一题。你没有写出这题,导致网友的国度简化货币系统的任务失败了。网友的国度的货币系统现在十分混乱。

网友的国度现今有两套货币系统「忘忧」和「网游」。为了方便使用,它们有一个共用的货币单位「𰻞」,而且二者都可以凑出价值任意整数𰻞的货币。

你现在希望用人民币来兑换一些网友的国度的货币。你需要兑换多次,每次恰好兑换 1 𰻞。

忘忧和网游的汇率互相独立,而且会动态改变。具体而言:

  • 第一次兑换忘忧,1 𰻞花费 a 元;由于网友的国度持有的忘忧变少了,因此其价格
    上涨:每兑换 1 𰻞,下一𰻞的价格涨价 b 元;
    形式化地,第 i 次兑换忘忧的价格是一𰻞 b(i −1) + a 元。
  • 第一次兑换网游,1 𰻞花费 c 元;由于网友的国度持有的网游变少了,因此其价格上涨:每兑换 1 𰻞,下一𰻞的价格涨价 d 元;形式化地,第 i 次兑换网游的价格是一𰻞 d(i −1) + c 元。

为了能够随机应变,你希望提前知道给出的 q 种 a, b, c, d 下,用 x 元钱分别最多能兑换多少𰻞。

每次询问相互独立

【输入格式】

第一行一个正整数 q,表示有 q 个询问。

接下来 q 行,每行 5 个正整数 a, b, c, d, x,分别表示忘忧、网游的汇率变化方式和你所拥有的钱。

【输出格式】

输出共 q q q 行,分别表示每次询问中你最多能兑换到多少𰻞。

1 ≤ a , b , c , d , x ≤ 1 0 18 , 1 ≤ q ≤ 1 0 5 1 ≤a, b, c, d, x ≤10^{18},1 ≤q ≤10^5 1a,b,c,d,x10181q105

solution

看到这种两个函数都是单增的,且最后总限制为 x x x

我就想到了 CSP-S 2021 T1,当时我就是采取的三分骗到了不错的分数。

这道题看起来似乎与二分/三分很有关联。

但是这两个函数不能拼成一个单调或有峰值的函数,能够直接做。

很妙的是,直接二分花费最大值 m i d mid mid

让两种物品的单个兑换最大花费不超过 m i d mid mid

先计算出每种物品最多能买多少个,最大的那个花费不超过 m i d mid mid,然后可以等差数列 O ( 1 ) O(1) O(1) 计算出。

具体而言,假设最多能买 i i i 个,即 b ( i − 1 ) + a ≤ m i d b(i-1)+a\le mid b(i1)+amid

花费则为 ∑ k = 1 i b ( k − 1 ) + a = ∑ k = 1 i b k + ( a − b ) ∗ i = ( b + b ∗ i ) i 2 + i ∗ ( a − b ) \sum_{k=1}^ib(k-1)+a=\sum_{k=1}^ibk+(a-b)*i=\frac{(b+b*i)i}{2}+i*(a-b) k=1ib(k1)+a=k=1ibk+(ab)i=2(b+bi)i+i(ab)

再把两种物品的花费求和,与 x x x 限制比较。

当然可能会剩下一点 r r r,也许还可以再买两种物品中的一个,特判一下。

最开始求最大花费不超过 m i d mid mid 的物品个数也请注意写法。——来自伞兵博主。

code

#include <bits/stdc++.h>
using namespace std;
#define int __int128
int q, x, a, b, c, d, ans;

void read( int &x ) {
    x = 0; char s = getchar();
    while( s < '0' or s > '9' ) s = getchar();
    while( '0' <= s and s <= '9' ) x = ( x << 1 ) + ( x << 3 ) + ( s - '0' ), s = getchar();
}

void print( int x ) {
    if( x > 9 ) print( x / 10 );
    putchar( x % 10 + '0' );
}

bool check( int v ) {
    int i = max( (int)0, ( v - a ) / b + 1 );
    int j = max( (int)0, ( v - c ) / d + 1 );
    if( i and b * ( i - 1 ) + a > v ) i --;
    if( j and d * ( j - 1 ) + c > v ) j --;
    int cost = ( i + 1 ) * b * i / 2 + i * ( a - b ) + ( j + 1 ) * d * j / 2 + j * ( c - d );
    if( cost > x ) return 0;
    int r = x - cost, tot = i + j;
    if( r >= b * i + a ) r -= b * i + a, tot ++, i ++;
    if( r >= d * j + c ) r -= d * j + c, tot ++, j ++;
    ans = max( ans, tot );
    return 1;
}

signed main() {
    freopen( "money.in", "r", stdin );
    freopen( "money.out", "w", stdout );
    read( q );
    while( q -- ) {
        read( a ), read( b ), read( c ), read( d ), read( x );
        int l = 0, r = x; ans = 0;
        while( l <= r ) {
            int mid = ( l + r ) / 2;
            if( check( mid ) ) l = mid + 1;
            else r = mid - 1;
        }
        print( ans );
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值