题目链接: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;
}