题目链接:https://codeforces.com/problemset/problem/1073/D
题目大意:
XXI Berland Annual Fair即将开幕,届时会有n个商户,围成一圈,顺时针由1到n标号。Polycarp想到时候至多花费T块钱。他先从1商户开始,如果当前商户的糖果单价小于等于他手上的金额,他会立刻买一个糖果,向下一家商户走去。如果不够钱则直接走向下一个商户。问最多他能买多少个糖果?
解题思路:
暴力:每次把走一圈最大开销求出来,计算在此开销下能走几圈,累加答案。直到不能买糖果为止。时间复杂度难求。
二分:每次二分找到会在哪个商户买不到糖果,删掉这个商户,删到能走完一圈。累计糖果数目与暴力做法没什么差别。由于二分 里面用树状数组求和,总复杂度为为O(n *log n * log n) (最多删掉n个点)。
经测验,暴力的时间比二分要快,可能是总钱数下降速度非常快(估计是指数级下降),而二分删点会多花一点时间。
暴力代码:
# include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long ll;
ll a[maxn];
ll n, T;
int main(){
std::ios::sync_with_stdio(false);
while(cin >> n >> T){
ll minn = 1e18 + 5;
for(int i = 1; i <= n; ++i){
cin >> a[i];
minn = min(minn, a[i]);
}
ll res = 0;
while(T >= minn){
ll sum = 0, tmp = 0;
for(int i = 1; i <= n; ++i){
if(T >= a[i]){
sum += a[i];
T -= a[i];
tmp++;
}
}
res += tmp + T / sum * tmp;
T %= sum;
}
cout << res << endl;
memset(a, 0, sizeof(a));
}
return 0;
}
二分+树状数组:
# include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long ll;
ll sum[maxn], a[maxn];
ll n, T, num;
int lowbit(int i){
return i & -i;
}
void add(int i, int x){
while(i <= n){
sum[i] += x;
i += lowbit(i);
}
}
ll ask(int i){
ll res = 0;
while(i){
res += sum[i];
i -= lowbit(i);
}
return res;
}
int findP(int l, int r, ll T){
while(l < r){
int mid = (l + r) / 2;
if(ask(mid) <= T) l = mid + 1;
else r = mid;
}
if(ask(l) > T) return l;
return 0;
}
int main(){
std::ios::sync_with_stdio(false);
while(cin >> n >> T){
for(int i = 1; i <= n; ++i){
cin >> a[i];
add(i, a[i]);
}
num = n;
ll res = 0;
while(num){
int pos = findP(1, n, T); //判断是否需要删点
if(pos){
add(pos, -a[pos]);
--num;
}
ll all = ask(n);
if(all <= T && all){ //判断删掉一个点之后能不能全都要
res += T / all * num;
T %= all;
}
//cout << res << endl;
}
cout << res << endl;
}
return 0;
}