把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P7519 [省选联考 2021 A/B 卷] 滚榜

题面传送门
看到数据范围想到可以状压。
然后以为是\(b\)的分配方式看着题目想了好久始终没有想到怎么把\(O(m)\)的枚举转移去掉。
\(f_{i,j,k}\)表示当前到了第\(i\)个队伍,集合为\(j\)\(b\)总量为\(k\)
发现这样子其实还是要枚举转移。
考虑怎么有最优分配方案,显然每个都给最少是最优的。设\(calc(x,y)\)\(a_y-a_x+(x<y)\)
则最优有\(b_i=\min(b_{i-1}+calc(i-1,i),b_{i-1})\)
那么差分后提前计算贡献就可以做了。
时间复杂度\(O(2^nn^2m)\)
code:

#include<cstdio>
#define N 14
#define M 500
#define I inline
#define ll long long
using namespace std;
int n,m,k,x,y,z,a[N],now,tot,maxn=1;ll dp[N][1<<N-1][M+5],ans;
I int calc(int x,int y){return a[y]>a[x]?0:(a[x]-a[y]+(x<y));}
int main(){
	freopen("1.in","r",stdin);
	register int i,j,h,k;scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) scanf("%d",&a[i]);
	for(i=2;i<=n;i++) maxn=(a[maxn]<a[i]?i:maxn);
	for(i=1;i<=n;i++) now=calc(maxn,i),now*n<=m&&(dp[i][1<<i-1][n*calc(maxn,i)]=1);
	for(i=1;i<(1<<n)-1;i++){
		now=i;tot=0;while(now) tot++,now-=now&-now;
		for(j=1;j<=n;j++){
			if(!(i&(1<<j-1)))continue;
			for(k=1;k<=n;k++){
				if(i&(1<<k-1)) continue;now=calc(j,k);
			    for(h=0;h<=m-now*(n-tot);h++)dp[k][i|(1<<k-1)][h+now*(n-tot)]+=dp[j][i][h]/*,printf("%d %d %d %d %d\n",j,i,h,k,now)*/;
			}
		} 
	}
	for(i=1;i<=n;i++){
		for(h=0;h<=m;h++) ans+=dp[i][(1<<n)-1][h];
	}
	printf("%lld\n",ans);
}
posted @ 2021-04-17 09:36  275307894a  阅读(100)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end