[JZOJ3920]噪音

description

FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声欢迎欢迎,热烈欢迎,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。FJ很讨厌噪音,所以FJ决定最多可以使用K次清空操作,每次清空操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。清空操作只能在噪音产生后执行。现在的问题是:FJ应该选择如何执行清空操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?


analysis
  • D P DP DP,考虑预处理辅助数组 g [ i ] [ j ] g[i][j] g[i][j]表示 i i i牛棚分割 j j j次的最小贡献

  • 对于 n u m [ j ] num[j] num[j],当然尽量分割成相等的 k + 1 k+1 k+1份,每一份取等差数列和的贡献

  • 就有 n u m [ j ] m o d    ( j + 1 ) num[j]\mod(j+1) num[j]mod(j+1)段值为 n u m [ j ] j + 1 + 1 {num[j]\over{j+1}}+1 j+1num[j]+1,剩下的段都是 n u m [ j ] j + 1 {num[j]\over{j+1}} j+1num[j]

  • 再设 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个牛棚分割 j j j次的最小值, f [ i ] [ j ] f[i][j] f[i][j]可从 f [ i − 1 ] [ k ] f[i-1][k] f[i1][k]转移得到


code
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#includestdio.h
#includestring.h
#includealgorithm
#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;
}
最新回复(0)
/jishucLwwuYTent0Yw2RmIr9yA_2B70AQGNXg9D0_2F_2FW1asC_2BIc_3D4858326
8 简首页