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卷]