题目链接:http://codeforces.com/contest/1073/problem/D
题目大意:有n个圆形摊位,每个摊位出售一些糖果,价格为ai, 你有t元钱,你只能从1->n的方向买糖果,只要手中的钱大等于当前摊位出售糖果价格ai,就必须购买,一次只能买一个,问你最多能够买多少个糖果。
第一圈: 5 2 5
剩余: 38 33 31 26
第二圈 5 2 5
剩余: 26 21 16 11
第三圈: 5 2 5
剩余: 11 6 4(不能买)
第四圈: 5 2 5
剩余: 4 (不能买) 4 2(不能买)
第五圈: 5 2 5
剩余: 2 (不能买) 2 0(不能买)
u=3+3+2+1+1=10本书
因为:1≤n≤2*10^5
1≤T≤10^18
所以暴力模拟肯定会T,所以我们可以用 扫描+删除 优化。
把所有的书存入set(以书的位置从小到大)
思路:
步骤1:如果当前的钱大于所有的书的价格和s,那么u+=(t/s)*st.size();
步骤2:然后再从1->n扫一遍,如果当前的钱不能买此书,那么以后一定不能再买,所以可以删除此书(set的重点操作lgn的复杂度)。
set<R, rube>::iterator p_=ps;//记录删除的位置
ps++;//先++,防止删除后再++,此时set已经没有了ps指的位置,++找不到位置
s-=p_->s;//书的价格和减去删除的书的价格
st.erase(p_);//删除书
步骤3:如果t<书价格的最小值,break;否则继续步骤1
边扫描边删除,可以大幅度降低复杂度。
思考:set真是好用
#include<bits/stdc++.h>
using namespace std;
#define LL long long
struct R
{
LL s;
LL i;
R(LL a, LL b){s=a, i=b;}
R(){}
};
struct rube
{
int operator()(const R& a, const R& b)
{
return a.i<b.i;
}
};
int cmp(R& a, R& b){return a.s>b.s;}
set<R, rube> st;
set<R, rube>::iterator ps;
LL n;
LL t=0, u=0, a, min_s=(1<<31)-1, s=0;
LL dfs()
{
while(1)
{
if(t>=s)//拥有的钱大等于当前s所有书的价格和
{
u+=(t/s)*st.size();
t%=s;
}
else
{
for(ps=st.begin();ps!=st.end();)
{
if(t>=ps->s)//拥有的钱大等于当前书的价格
{
t-=ps->s;
u++;
ps++;
}
else //拥有的钱小于当前书的价格
{
set<R, rube>::iterator p_=ps;
ps++;
s-=p_->s;
st.erase(p_);
}
}
}
if(t<min_s)
break;
}
}
int main()
{
scanf("%lld%lld",&n,&t);
for(int i=0;i<n;i++)
{
scanf("%lld",&a);
s+=a, min_s=min(min_s, a);
st.insert(R(a, i));
}
dfs();
cout<<u<<endl;
return 0;
}