Jzoj5245 Competing Souls

来源:互联网 发布:香水时代的淘宝店怎么 编辑:程序博客网 时间:2024/06/03 13:36

温馨提示:本文附带bgm

某日,竞赛班的学生来到了一家糖果店。
店里卖着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 longusing 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());}
原创粉丝点击