D e s c r i p t i o n Description Description
给定 n n n头牛和 m m m个房间,第 i i i头牛会进入 p i p_i pi这个房间,在 x x x这头牛进入 y y y这个房间时, y y y里所有的牛以及这头牛自己都会为 x x x这头牛欢呼
这些欢呼会消耗代价,你可以选择在某头牛欢呼完之后清空这个牛棚,求最小欢呼总代价
数据范围: n ≤ 1 0 6 , m ≤ 1 0 2 , k ≤ 5 × 1 0 2 n\leq 10^6,m\leq 10^2,k\leq 5\times 10^2 n≤106,m≤102,k≤5×102
S o l u t i o n Solution Solution
首先注意到这些牛棚是互不相关的
于是考虑 d p dp dp
设 f [ i ] [ j ] f[i][j] f[i][j]表示处理到第 i i i个牛棚,恰好用了 j j j次清除的最小代价
问题就转化称为对于每个牛棚,每个选择清除 a p a_p ap次产生 b p b_p bp的代价,要使 ∑ a = k \sum a=k ∑a=k的前提下 b p b_p bp最小(为什么是 ∑ a = k \sum a=k ∑a=k呢?因为删除肯定越多越好,恰好为 k k k次必然更优)
于是 d p dp dp
f [ i ] [ j ] = m i n { f [ i − 1 ] [ k ] + w e i g h t ( e d [ i ] , j − k + 1 ) } f[i][j]=min\{f[i-1][k]+weight(ed[i],j-k+1)\} f[i][j]=min{f[i−1][k]+weight(ed[i],j−k+1)}
w e i g h t ( n , q ) weight(n,q) weight(n,q)表示将一个有 n n n头牛的牛棚分为 q q q份(即删除 q − 1 q-1 q−1次)的代价,这个可以 O ( 1 ) O(1) O(1)算,具体解释请移步 w y c wyc wyc大爷的博客
于是我们就 O ( m 2 k ) O(m^2k) O(m2k)解决了这道题
C o d e Code Code
#include<cctype>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;;
LL f[101][501],n,m,k,x,ed[101];
inline LL read()
{
char c;LL d=1,f=0;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline LL weight(LL gs,LL cs)
{
LL ls=gs/cs,ms=gs%cs;
return (1+ls)*ls/2*cs+ms*(ls+1);
}
signed main()
{
memset(f,0x3f,sizeof(f));
n=read();m=read();k=read();
for(register int i=1;i<=n;i++) x=read(),ed[x]++;
f[0][0]=0;
for(register int i=1;i<=m;i++)
for(register int j=0;j<=k;j++)
for(register int p=0;p<=j;p++)
f[i][j]=min(f[i][j],f[i-1][p]+weight(ed[i],j-p+1));
printf("%lld",f[m][k]);
}