样例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,⌊ciC⌋dihi>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(⌊ciC⌋dihi)>DjHj
-
如果对于每个C,能求出前者,则问题解决了。如何求解?
-
对于每个C如果遍历全部将造成很高的复杂度,能否发现一些有用的性质并避免重复计算呢?
-
- IDEA:通过一定的方式预处理求解!不要固定一个C重复求解
- 和预处理因数类似!逆向思考。
- 看倍数能够达到哪些点
- 也可以这样思考:对于一给定的C,如何才能使得左侧的式子最大化?:结果一定是一定是某个 c i c_i ci的倍数达到!而这样的 c i c_i ci如何找?从单种unit的一个开始往后延拓(结果是最近倍数的最大值)
- IDEA:通过一定的方式预处理求解!不要固定一个C重复求解
-
对于每个可能的 c i , C = c i c_i,C=c_i ci,C=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;
}