【NOIP2017模拟8.8A组】Competing Souls

来源:互联网 发布:mk淘宝代购的是真是假 编辑:程序博客网 时间:2024/06/06 15:59

Description:

某日,竞赛班的学生来到了一家糖果店。
店里卖着M袋糖果,第i袋糖果里装有i颗糖,价格为i¥。
有N个学生对这些糖果产生了兴趣,于是迅速站成一排,且将他们编号为1到N。其中第i个学生带着a[i]¥。每一轮,他们按顺序买糖果(每一轮每个人只会买一袋)。由于体内的竞争之魂与超乎常人的不服输精神,当前学生买的这袋糖果一定会比上一个人多(当然,第一次可以随便买)。如果第N个人买了糖果,那么就到第1个人开始下一轮。
然而,钱和糖果都有限,总是要停下来的。可以在任意时刻停止购买糖果,但是最后一次必须是第N个人购买。
现在他们想知道,最终所有人购买糖果数之和最大可以是多少。(当然可以一袋都不买~)
1≤T≤5 2≤N≤500,000 N≤M≤5,000,000 1≤a[i]≤ (m*(m+1)/2)

题解:

贪心。
一种很优的贪心:

1.可以先枚举买多少轮,当前有k轮,一个比较容易想到的方法就是先按1、2、3、4……这样子放,然后a是还有剩余的,于是倒着拔高,直到不能不能拔高为止。时间复杂度:O((m / n)^2 * n)

2.假设要拔高i在j层的数,如果它的后面已经全部拔高到顶,即m,m-1,m-2这样子,那么当前就可以拔高(m - b[i])(前提是a够),b[i]为i在第k层的原来的大小,我们发现这是恒定的。那么假设要把整个第j层给拔高到顶,实际上花费和第j+1层是一样的。于是就可以直接算后面有多少层能够直接拔高到顶,假设后u层能够拔高到顶,对于倒数第u+1层,你可以想象再把它们整体拔高到中间,这时候一定有a为0,于是最后还能把倒数第u+2层拔高一点,a就用到极限了。

时间复杂度:O(T * (m / n) *n) =O(T*m)

Code:

#include<cstdio>#define ll long long#define fo(i, x, y) for(ll i = x; i <= y; i ++)#define fd(i, x, y) for(ll i = x; i >= y; i --)#define max(a, b) ((a) > (b) ? (a) : (b))#define min(a, b) ((a) < (b) ? (a) : (b))using namespace std;ll T, n, m, ans, a[500005], c[500005];void read(ll &x){       char c = ' ';       for (c=getchar();c<'0' || c>'9';c=getchar());       x = 0;       for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;}int main() {    freopen("compete.in", "r", stdin);    freopen("compete.out", "w", stdout);    for(scanf("%lld", &T); T; T --) {        scanf("%lld %lld", &n, &m);        fo(i, 1, n) read(a[i]), c[i] = a[i];        ll s = 0;        fo(hang, 1, m / n) {            if(hang * n > m) break;            int ab = 0;            fo(i, 1, n) {                a[i] = c[i] - (2 * i + (hang - 1) * n) * hang / 2;                if(a[i] < 0) {                    ab = 1; break;                }            }            if(ab) break;            ll x = hang * n; ans = (1 + hang * n) * hang * n / 2;            ll min = hang;            fo(i, 1, n) min = min(min, a[i] / (m - x));            fo(i, 1, n) ans += min * (m - x), a[i] -= min * (m - x);            if(min != hang) {                ll mi = m - x;                fd(i, n, 1)                    mi = min(mi, a[i]), a[i] -= mi, ans += mi;                if(min != hang - 1) {                    fd(i, n, 1)                        mi = min(mi, a[i]), a[i] -= mi, ans += mi;                }            }            s = max(s, ans);        }        printf("%lld\n", s);    }}
原创粉丝点击