description
FJ有M個牛棚,編號1至M,剛開始所有牛棚都是空的。FJ有N頭牛,編號1至N,這N頭牛按照編號從小到大依次排隊走進牛棚,每一天只有一頭奶牛走進牛棚。第i頭奶牛選擇走進第p[i]個牛棚。由於奶牛是羣體動物,所以每當一頭奶牛x進入牛棚y之後,牛棚y裏的所有奶牛們都會喊一聲“歡迎歡迎,熱烈歡迎”,由於聲音很大,所以產生噪音,產生噪音的大小等於該牛棚裏所有奶牛(包括剛進去的奶牛x在內)的數量。FJ很討厭噪音,所以FJ決定最多可以使用K次“清空”操作,每次“清空”操作就是選擇一個牛棚,把該牛棚裏所有奶牛都清理出去,那些奶牛永遠消失。“清空”操作只能在噪音產生後執行。現在的問題是:FJ應該選擇如何執行“清空”操作,才能使得所有奶牛進入牛棚後所產生的噪音總和最小?
analysis
-
,考慮預處理輔助數組表示牛棚分割次的最小貢獻
-
對於,當然儘量分割成相等的份,每一份取等差數列和的貢獻
-
就有段值爲,剩下的段都是
-
再設表示前個牛棚分割次的最小值,可從轉移得到
code
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define ha 1000000007
#define ll long long
#define reg register ll
#define fo(i,a,b) for (reg i=a;i<=b;++i)
#define fd(i,a,b) for (reg i=a;i>=b;--i)
using namespace std;
ll f[105][505],g[105][505];
ll num[105];
ll n,m,k,ans=ha;
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline ll get(ll x){return x*(x+1)/2;}
int main()
{
freopen("T3.in","r",stdin);
n=read(),m=read(),k=read();
fo(i,1,n)++num[read()];
fo(i,1,m)
{
g[i][0]=get(num[i]);
fo(j,1,k)
{
ll tmp1=num[i]/(j+1),tmp2=num[i]%(j+1),tmp3=j+1-tmp2;
g[i][j]=tmp3*get(tmp1)+tmp2*get(tmp1+1);
}
}
fo(i,1,m)fo(j,0,k)
{
f[i][j]=f[i-1][j]+g[i][0];
fo(l,0,j)f[i][j]=min(f[i][j],f[i-1][j-l]+g[i][l]);
}
fo(i,0,k)ans=min(ans,f[m][i]);
printf("%lld\n",ans);
return 0;
}