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);
}