题意:
给定n个店铺,每个店铺买的东西有个价格a_i,数量有无限个,然后主人公从1号开始走到n号,每走到一个店铺,只要他的钱大于价格,他就要买,然后重复上述过程,直到他不能购买,输出他能买的物品件数;
思路:
直接模拟的话,必然不可行,但是我们知道模拟时到达一个位置后买不起这里的东西就可以把这个店铺给踢了(好坏!),然后继续后面的过程,如果要是我的钱足够买完一轮的话,我会除一下,取下膜,这样就能快速知道购买的个数,如果不能买完一轮,说明又有我买不起的东西了,然后就要把他踢走;
如上述过程所述,我们应该怎么高效维护这个过程呢,如果我们知道其中有我们买不起的东西,我们要快速的找到他的位置,显然遍历是不行的,这时候我们就会想到预处理前缀和,然后此时整个序列就是有序的了,然后可以二分查找第一个买不起的位置;
但是找到她以后我还要绑了她,所以我还要一个能维护前缀和并支持修改的数据结构,显然是树状数组;
那这样解题过程就是每次找一个我买不起的位置,删去,然后计算这一轮我能买多少个,知道所有的我都买不起了(全踢出去了,n_为0),输出答案;
思考:有的题或许不能一上来就得到正确的思路,这时我们可以先从暴力的角度考虑,然后发现一些问题以及可优化的点,然后运用其他方式优化掉;正如这个C题https://blog.csdn.net/xiang_6/article/details/83686299,一开始也是想到的枚举区间,但是显然不行,所以我们去二分这个长度然后check,
#include<bits/stdc++.h>
using namespace std;
#define out fflush(stdout)
#define fast ios::sync_with_stdio(0),cin.tie(0);
#define FI first
#define SE second
typedef long long ll;
typedef pair<int,int> P;
const int maxn = 2e5 + 7;
const int INF = 0x3f3f3f3f;
ll c[maxn+7];
void add(int id, ll v) {
for(int i = id; i <= maxn; i += (i&-i)) {
c[i] += v;
}
}
ll sum(ll id) {
ll res = 0;
for(int i = id; i > 0; i -= (i&-i)) {
res += c[i];
}
return res;
}
int n;
ll m;
ll a[maxn];
int main() {
scanf("%d%lld", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
add(i, a[i]);
}
int n_ = n;
ll ans = 0;
while(1) {
int l = 1, r = n;
int pos = -1;
while(l <= r) {
int mid = (l + r) >> 1;
ll t = sum(mid);
if(t > m) {
pos = mid;
r = mid - 1;
}
else {
l = mid + 1;
}
}
if(pos == -1) {
ll t = sum(n);
ans += (m / t)*n_;
m %= t;
}
else {
add(pos, -a[pos]);
n_--;
}
if(n_ == 0) break;
}
cout << ans << endl;
return 0;
}