[省选联考 2021 A/B 卷] 滚榜 题解
[省选联考 2021 A/B 卷] 滚榜
看见数据范围 \(n\le 13\),马上就想到状压了,最初的想法是设 \(f_{s,i,k,j}\) 表示已经公布了队伍的集合为 \(s\),最新一个公布的是 \(i\),已经用了的 \(b_i\) 总和为 \(k\),上一次用的 \(b_i\) 为 \(j\),这样子转移必须带一个 \(O(m^2)\)。
毕竟题目问的是有几种可能的最终排名,和 \(b\) 没有关系,所以直接来考虑最优的分配 \(b\) 的方式,假设最终的排名序列已经确定为从低到高 \(p_{1},p_{2},p_{3}...p_{n}\),那么每一个 \(a_{p_i} < a_{p_{i-1}}\),就要给它的 \(b_i\) 加上 \(a_{p_{i-1}}-a_{p_i}\)。当然若编号更大的话还要多加一,还要注意新加上 \(b_i\) 的数要大于原榜上的第一名。这样子最后总和小于等于 \(m\) 的话就是合法的排名,小于的话加在最后一个就行了,我们可以用全排列初步实现一下,复杂度 \(O(n!n)\),可以拿六十分。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = (1<<8),M=505;
int a[15],p[15];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
int id=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>a[id])id=i;
p[i]=i;
}
ll ans=0;
do{
int sum=0,lst=0;
for(int i=1;i<=n;i++){
if(a[p[i]]>=a[p[i-1]]){
if(a[p[i]]==a[p[i-1]]&&p[i]>p[i-1]&&p[i-1]!=0)lst++;
}else{
int x=a[p[i-1]]-a[p[i]];
lst+=x;
if(p[i]>p[i-1]&&p[i-1])lst++;
}
if(a[p[i]]+lst<a[id]&&p[i]!=id){
lst+=a[id]-a[p[i]]-lst;
}
if(a[p[i]]+lst==a[id]&&p[i]>id)lst++;
sum+=lst;
//cout<<lst<<" ";
if(sum>m)break;
}
//cout<<endl;
if(sum<=m)ans++;
}while(next_permutation(p+1,p+1+n));
cout<<ans;
return 0;
}
回到状压的想法上来,为了为避免 \(m^2\) 的枚举,我们必须这样子考虑,一个数的 \(b_i\) 被确定后,它就会给后面的每一个数都产生贡献,即 \((n-p_i)*b_i\),那我们就考虑提前把这个贡献算了,设 \(f_{s,i,k}\) 表示已经公布的集合为 \(s\),上一个是 \(i\),已经产生了 \(k\) 的总贡献。转移比较简单,复杂度 \(O(2^nn^2m)\)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = (1<<13),M=505;
int a[15],p[15],id[N|1];
ll f[N+10][15][M],ans;
int lowbit(int x){
return x&(-x);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
int id=0;a[0]=-1;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]>a[id])id=i;
id[1<<(i-1)]=i;
}
for(int i=1;i<=n;i++){
int x=n*(a[id]-a[i]+(id<i));
if(x<=m)f[1<<(i-1)][i][x]=1;
}
for(int s=1;s<(1<<n);s++){
int px=popcount(s);
for(int t=s;t;t-=lowbit(t)){
for(int k=0;k<=m;k++){
int j=lowbit(t);
for(int i=1;i<=n;i++){
if((s>>(i-1))&1)continue;
int sum=k+(n-px)*max(0,a[i]-a[j]+(j<i));
if(sum<=m)f[s|(1<<(i-1))][i][sum]+=f[s][j][k];
}
}
}
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++){
ans+=f[(1<<n)-1][i][j];
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号