温馨提示:本文附带bgm border="0" width="1" height="1" src="//music.163.com/outchain/player?type=2&id=460520976&auto=1&height=32">
某日,竞赛班的学生来到了一家糖果店。
店里卖着M袋糖果,第i袋糖果里装有i颗糖,价格为i。
有N个学生对这些糖果产生了兴趣,于是迅速站成一排,且将他们编号为1到N。其中第i个学生带着a[i]¥。每一轮,他们按顺序买糖果(每一轮每个人只会买一袋)。由于体内的竞争之魂与超乎常人的不服输精神,当前学生买的这袋糖果一定会比上一个人多(当然,第一次可以随便买)。如果第N个人买了糖果,那么就到第1个人开始下一轮。
然而,钱和糖果都有限,总是要停下来的。可以在任意时刻停止购买糖果,但是最后一次必须是第N个人购买。
现在他们想知道,最终所有人购买糖果数之和最大可以是多少。(当然可以一袋都不买~)
我们发现有一个限制,一定要最后是第n个人购买
所以我们可以考虑将其视为一个矩阵
首先枚举这个矩阵的行数i,让后依次填上1~i*n
让后开始考虑将几行的值改变
例如这样:n=3,m=10时
1 2 3
4 5 6
7 8 9
变成
1 2 3
4 5 7
8 9 10
那么我们来考虑如何变换
首先显然,将两行都+1和将一行+2是等价的,所以我们每次都将一整行直接加到最大(除非不够钱)
首先计算出,每一列减去已经填好得数后得到的最小的那个,记为dx
让后根据这个数来计算最多有多少行可以被整体加到最大
显然这个行数h=min(i,dx/(m-i*n))
剩下的,在第n-h行中,每一个数都尽可能加大,除非不够(参考上面那个例子的第二行)
这样整体复杂度为
O(n×mn)=O(m)
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define LL long long
using namespace std;
int n,m;
LL s[500010],w[500010],A,dx,S,h;
int _18520(){
scanf("%d%d",&n,&m); A=0;
for(int i=1;i<=n;++i) scanf("%lld",s+i);
for(int i=1,z=m/n;i<=z;++i){
dx=1ll<<60; S=(n*i+1ll)*(LL)n*i>>1;
for(int j=1;j<=n;++j){
s[j]-=i*n-n+j;
dx=min(dx,s[j]);
}
if(dx<0) break;
h=min((LL)i,dx/(m-i*n));
S+=n*h*(m-i*n); dx=h*(m-i*n);
for(int j=1;j<=n;++j) w[j]=s[j]-dx;
dx=m-i*n;
for(int j=i-h;j>=i-h-1&&j;--j)
for(int k=n;k;--k){
dx=min(w[k],dx);
S+=dx; w[k]-=dx;
if(dx==0) break;
}
A=max(A,S);
}
printf("%lld\n",A);
}
int main(){
freopen("compete.in","r",stdin);
freopen("compete.out","w",stdout);
int T; for(scanf("%d",&T);T--;_18520());
}