正题
P7519
题目大意
n个队伍,排名先按分数排序再按编号排序,每个队伍有一个初始分数 aia_iai,和一个附加分数 bib_ibi
对于一个合法的 bib_ibi 序列,按 bib_ibi 大小排序,从小到大把每个 bib_ibi 加进对应的 aia_iai 中,且每个 bib_ibi 加入后对应的点会成为排名第一的点
现在告诉你所有 bib_ibi 的和 m,问你对于所有合法的 bib_ibi,最后的排名有多少种
解题思路
考虑状压DP,设 fS,i,j,kf_{S,i,j,k}fS,i,j,k 为当前所有点是否添加的状态为 SSS,最后添加的点为 iii,bi=jb_i=jbi=j,已经加了的 bbb 和为 kkk 的方案数
直接转移时间复杂度为 O(2nn2m2)O(2^nn^2m^2)O(2nn2m2),会TLE
考虑到答案的计算之和最后的排名有关,也就是和加入 bbb 的顺序有关(下文所有顺序都是指加入顺序中的先后),那么对于一种加入顺序,让所有点的 bib_ibi 尽量小(除最后一个点)
因为 bib_ibi 是单调递增的,所以考虑一个点加入 bib_ibi 时,让后面点的 bbb 都加上 bib_ibi,那么就可以保证 bib_ibi 是单调递增的
综上,可以优化掉最后添加的点 iii 的附加值 bib_ibi,那么设 fS,i,kf_{S,i,k}fS,i,k 表示状态为S,最后添加的点为 iii ,bib_ibi 的和为 kkk ,然后直接转移即可
时间复杂度 O(2nn2m)O(2^nn^2m)O(2nn2m),因为很多状态都跑不到,所以实际上能过
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 14
using namespace std;
ll n,m,mx,sum,ans,a[N],f[1<<N][N][505];
int main()
{scanf("%lld%lld",&n,&m);for(ll i=1;i<=n;++i){scanf("%lld",&a[i]);if(a[i]>a[mx])mx=i;}f[0][mx][0]=1;//要保证第一个点加到比最大的点大for(ll S=0;S<(1<<n)-1;++S){sum=0;for(ll i=1;i<=n;++i)if(!(S&(1<<i-1)))sum++;//计算后面有多少个点for(ll k=0;k<=m;++k)for(ll i=1;i<=n;++i) if(f[S][i][k])for(ll j=1;j<=n;++j)if(!(S&(1<<j-1)))if(k+sum*max(a[i]-a[j]+(j>i),0ll)<=m)f[S|(1<<j-1)][j][k+sum*max(a[i]-a[j]+(j>i),0ll)]+=f[S][i][k];}for(ll i=1;i<=n;++i)for(ll j=0;j<=m;++j)//多出来的部分放到最后一个点ans+=f[(1<<n)-1][i][j];printf("%lld",ans);return 0;
}