3920. 【NOIP2014模拟11.3】噪音

Description

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

Input

第一行,N、M、K。
接下来有N行,每行一个整数,第i行是p[i]。

Output

最小的噪音总和。

Sample Input

输入1:
5  1  2
1
1
1
1
1
输入2:
11  2  3
1
2
1
2
1
2
1
2
1
2
1

Sample Output

输出1:
7
样例解释1:
第1头奶牛进入牛棚且产生噪音后,“清空”牛棚。第3头奶牛进入牛棚且产生噪音后,再次“清空”牛棚。5头奶牛产生的噪音依次是:1,1,2,1,2。如果没有“清空”操作,5头奶牛产生的噪音依次是:1,2,3,4,5。

输出2:
18
样例解释2:
第3头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。第7头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。
第6头奶牛进入牛棚2且产生噪音后,“清空”牛棚2。

Data Constraint

对于40%的数据,M=1。
对于60%的数据,1<=N <= 1000。
对于80%的数据,1<=N <= 50000。
对于100%数据, 1<=N <= 1000000, 1<=M<=100,  1<=K<= 500。

Solutoin

对于每一个牛棚是独立的。可以预处理出第i个牛棚用j次的代价,那么问题转化为将k次操作分给i个牛棚,每次操作对应着i的一个值,求最大代价。

那么直接dp,设f[i][j]表示前i个牛棚一共用了j次,则f[i][j]=min{ f [ i-1 ][ j-k ]+cost [ i-1 ][ k ]}

时间O(mk^2)。

实际上可以优化到k log m。

用堆维护所有牛棚中哪一个多用一次贡献最大(做差),一共取k次,即k log m。
 

Code1

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 1000010
using namespace std;

I rd(I &x){
	x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
I n,m,k,x,t[110];
ll f[110][510],g[110][510],ans=(ll)1e12,y,r;
I main(){
	rd(n),rd(m),rd(k);
	F(i,1,n){
		rd(x);
		t[x]++;
		if(t[x]==1) t[0]++;
	}
	F(i,1,100) if(!t[i]){
		F(j,i+1,100) if(t[j]){
			t[i]=t[j],t[j]=0;break;
		}
	}
	F(i,1,t[0]){
		F(j,0,k){
			y=t[i]/(j+1),r=t[i]%(j+1);
			f[i][j]=(ll)r*((2+y)*(y+1)/2)+(ll)(j+1-r)*((y+1)*y/2);
		}
	}
	mem(g,0x3f);
	F(i,0,k) g[1][i]=f[1][i];
	F(i,2,t[0]){
		F(j,0,k){
			F(l,0,j) if(g[i-1][j-l]+f[i][l]>=0) g[i][j]=min(g[i][j],g[i-1][j-l]+f[i][l]);
		}
	}
	F(i,1,k) g[t[0]][i]=min(g[t[0]][i],g[t[0]][i-1]);
	printf("%lld\n",g[t[0]][k]);
	return 0;
}

Code2

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define F(i,a,b) for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define N 1000010
#define M 110
using namespace std;
I rd(I &x){
	x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
I n,m,k,x,d[M*4],c[M];
ll ans,a[M],b[M],t[M];
ll ask(I x,I y){
	ll p=x/y,r=x%y;
	return (ll)r*((2+p)*(p+1)/2)+(ll)(y-r)*((p+1)*p/2);
}
ll calc(I x){return a[d[x]]-b[d[x]];}
void down(I x){
	I y=x<<1;
	while(y<=d[0]&&calc(y)>calc(x)||y+1<=d[0]&&calc(y+1)>calc(x)){
		y+=(calc(y)<calc(y+1));
		swap(d[x],d[y]);
		x=y;y=x<<1;
	}
}
void up(I x){
	while(x>1&&calc(x)>calc(x>>1)){swap(d[x],d[x>>1]);x>>=1;}
}
void ins(I x){
	d[++d[0]]=x;
	up(d[0]);
}
I main(){
	rd(n),rd(m),rd(k);
	F(i,1,n){
		rd(x);
		t[x]++;
	}
	F(i,1,m) if(t[i]){
		a[i]=(t[i]+1)*t[i]/2;
		b[i]=ask(t[i],c[i]=2);
		ins(i);ans+=a[i];
	}
	F(i,1,k){
		ans-=a[d[1]]-b[d[1]];
		a[d[1]]=b[d[1]],b[d[1]]=ask(t[d[1]],++c[d[1]]);
		down(1);
	}
	printf("%lld\n",ans);
	return 0;
}

作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/103499662

发布了199 篇原创文章 · 获赞 201 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/103499662