题解 P7519 [省选联考 2021 A/B 卷] 滚榜
前言
纪念一下这场爆炸的省选唯一 AC 的题目。
同时是第一次在正式考场上 AC 动态规划。
同时是第一次在正式考场上 CE(还是本应 AC 的 Day1 T1)。
大概是本场第三简单的题。
应该比去年 Day2 T1 简单。
分析
看了一眼范围估计是状压。
开始分析的时候容易想到设计状态为
冷静看题。发现只用求排名的总方案数,而与
对于某一确定的排列方式
根据这一结论,直接使用全排列可以获得 60 pts 的好成绩。
回到状压。考虑对贡献进行简单变形:
最后统计
时间复杂度为
上述方法忽略的细节是相等时的按编号排序,实现时应注意。
代码
upd on 2021.4.24
根据 UOJ 数据修复了代码实现的一点小问题,目前在 UOJ 上可通过。
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn=13,maxm=500;
int n,m,all,t,a[maxn+1],no[1<<maxn|1];
long long f[1<<maxn|1][maxn+1][maxm+1],ans;
inline int lowbit(int x)
{
return x&(-x);
}
int main()
{
scanf("%d%d",&n,&m);
all=(1<<n)-1;
a[0]=-1;
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if(a[i]>a[t])
t=i;
no[1<<(i-1)]=i;
}
for(int i=1;i<=n;++i)
{
int target=n*(a[t]-a[i]+(t<i));
if(target<=m)
f[1<<(i-1)][i][target]=1;
}
for(int i=1;i<all;++i)
{
int popcount=0;
for(int j=1;j<=maxn;++j)
if(i&(1<<(j-1)))
++popcount;
for(int t=i;t;t-=lowbit(t))
for(int sum=0;sum<=m;++sum)
{
int pos=no[lowbit(t)];
for(int j=1;j<=n;++j)
if(!(i&(1<<(j-1))))
{
int target=sum+(n-popcount)*max(0,(pos<j)+a[pos]-a[j]);
if(target<=m)
f[i|(1<<(j-1))][j][target]+=f[i][pos][sum];
}
}
}
for(int i=0;i<=m;++i)
for(int j=1;j<=n;++j)
ans+=f[all][j][i];
printf("%lld",ans);
return 0;
}