P7519-[省选联考 2021 A/B 卷]滚榜【状压dp】
正题
题目链接:https://www.luogu.com.cn/problem/P7519
题目大意
nnn个队伍,队伍之间按照得分从小到大排名,得分相同的按照编号从小到大排。开始时每个队伍有个初始得分aia_iai,和一个额外分bib_ibi,主持人会按照bib_ibi不降的顺序让每个队伍的得分加上bib_ibi,并且要求每次加上后这个队伍都变成第一名。
已知每个队伍的初始分aaa和额外分的和mmm,求有多少种公布额外分的序列。
1≤n≤13,1≤m≤500,0≤ai≤1041\leq n\leq 13,1\leq m\leq 500,0\leq a_i\leq 10^41≤n≤13,1≤m≤500,0≤ai≤104
解题思路
显然地一点是我们考虑一个序列合法时bib_ibi的和的最小值,然后和mmm进行比较
开始思路时卡了,假设已经排好了一部分,我们需要把一个新的排在后面,此时会有两个限制:
显然记录上这两个限制进行的dpdpdp并不方便,考虑如何去掉一个限制,因为bib_ibi是我们可以任意调整的,并且要求递增,可以每次操作都让后面所有数字的bib_ibi都同时加值,这样就不需要考虑第二个限制了。
然后是第一个限制如何处理,注意到我们在刚刚的情况下,假设限制最后公布的一个是iii,而下一个公布的是jjj,那么此时有bj=bib_j=b_ibj=bi,所以此时两个数加上bbb之后的差值不变,所以直接拿ai−aja_i-a_jai−aj就可以知道后面的bjb_jbj要加上多少了。
那么做法已经显然了,设fs,i,jf_{s,i,j}fs,i,j表示现在已经插入的数状态为sss,bib_ibi的和为iii,目前最后一个公布的是jjj,然后O(n)O(n)O(n)转移即可。
时间复杂度:O(2nmn2)O(2^nmn^2)O(2nmn2),实际上很多状态是不会到达的,所以能过。
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=13; ll n,m,ans,a[N],c[1<<N],f[1<<N][501][N]; signed main() {scanf("%lld%lld",&n,&m);ll pos=0;for(ll i=0;i<n;i++){scanf("%lld",&a[i]);if(a[i]>a[pos])pos=i;}f[0][0][pos]=1;ll MS=(1<<n);for(ll s=1;s<MS;s++)c[s]=c[s-(s&-s)]+1;for(ll s=0;s<MS;s++)for(ll k=0;k<=m;k++)for(ll i=0;i<n;i++){if(!f[s][k][i])continue;for(ll j=0;j<n;j++){if((s>>j)&1)continue;ll w=max(a[i]-a[j]+(j>i),0ll)*(n-c[s]);if(k+w>m)continue;f[s^(1<<j)][k+w][j]+=f[s][k][i];}}for(ll i=0;i<=m;i++)for(ll j=0;j<n;j++)ans+=f[MS-1][i][j];printf("%lld\n",ans);return 0; }总结
以上是生活随笔为你收集整理的P7519-[省选联考 2021 A/B 卷]滚榜【状压dp】的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 抽屉锁怎么撬开 抽屉锁打开方法
- 下一篇: P7516-[省选联考2021A/B卷]