Codeforce Educational Codeforces Round 125 (Rated for Div. 2) D题(预处理+调和级数在下取整中的妙用)

在这里插入图片描述
在这里插入图片描述样例1

3 10
3 4 6
5 5 5
10 3 4
3
8 3
5 4
10 15

输出1

5 3 -1 

输入2

5 15
14 10 3
9 2 2
10 4 3
7 3 5
4 3 1
6
11 2
1 1
4 7
2 1
1 14
3 3

输出2

14 4 14 4 7 7 

  • 首先注意此题的数据范围

思路分析

  • 看数据范围很容易想到做法:给定C,能否判断能否使用C枚硬币打败怪兽。二分即可

  • 但是给定C如何判断是否能打败怪兽?

  • 需要满足 ∃ i , ⌊ C c i ⌋ d i h i > D j H j \exists i,\lfloor \frac{C}{c_i} \rfloor d_ih_i > D_jH_j i,ciCdihi>DjHj

  • 只需要 m a x i ( ⌊ C c i ⌋ d i h i ) > D j H j max_i(\lfloor \frac{C}{c_i} \rfloor d_ih_i) > D_jH_j maxi(ciCdihi)>DjHj

  • 如果对于每个C,能求出前者,则问题解决了。如何求解?

  • 对于每个C如果遍历全部将造成很高的复杂度,能否发现一些有用的性质并避免重复计算呢?

    • IDEA:通过一定的方式预处理求解!不要固定一个C重复求解
      • 和预处理因数类似!逆向思考。
      • 看倍数能够达到哪些点
      • 也可以这样思考:对于一给定的C,如何才能使得左侧的式子最大化?:结果一定是一定是某个 c i c_i ci的倍数达到!而这样的 c i c_i ci如何找?从单种unit的一个开始往后延拓(结果是最近倍数的最大值)
  • 对于每个可能的 c i , C = c i c_i,C=c_i ciC=ci如果限制仅取一个该种类型的unit,可以计算出前者。

  • 对于每个可能的 c i c_i ci ⌊ C c i ⌋ \lfloor \frac{C}{c_i} \rfloor ciC增大时仅限于C是 c i c_i ci的倍数的时候,其余的不会增大。因此对每个可能的 c i c_i ci均使C从 c i c_i ci开始一直扩大相应的倍数。

  • 最后,对不是任何 c i c_i ci倍数的部分,采用当前的值(一定为0)和前面的值取最大值即可。

AC代码

#include<iostream>
#include<vector>
#include<string>
#include<set>
#include<algorithm>
#include<map>
#include<queue>
#include<math.h>
#define int long long
using namespace std;
long long c[300001];
long long d[300001];
long long h[300001];
long long F[1000001];   //给定一个可选c的条件下,求[C/C_i]*hi*di可能的最大值



signed main()
{
    int n,C;
    cin >> n >> C;
    int m;
    set<int> c_occ;
    for(int i = 0;i<n;i++) 
    {
       cin >> c[i] >> d[i] >> h[i];
       F[c[i]] = max(F[c[i]],d[i]*h[i]);      //仅雇佣一个的[C/C_i]*hi*di可能的最大值
       c_occ.insert(c[i]);
    }
    for(set<int>::iterator iter = c_occ.begin();iter!=c_occ.end();iter++)
    {
        int c = *iter;  //倍数更新
        for(int  j = c;j<=C;j+=c) F[j]=max(F[j],(j/c)*(F[c])); 
    }
    cin >> m;
    //1到C更新最大值
    for(int i  = 1;i<=C;i++) F[i]=max(F[i],F[i-1]);
    for(int  j = 0;j<m;j++)
    {
        int D,H; cin >> D >> H;
        int low  = 1; int high = C+1;
        while(low < high)   
        {
            int mid = (low+high)>>1;
            if(F[mid] <= (long long)D*H) low=mid+1;
            else high = mid;
        }
        if(low>=1 && low<=C) cout << low;
        else cout << -1;
        if(j!=m-1) cout << " ";
    }
    cout << endl;
    system("pause");
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值