滚榜
题解
n
!
n!
n!的暴力竟然有60pts!!!
首先,
n
⩽
13
n\leqslant 13
n⩽13这个范围是很小的,我们很容易想到状压dp。
由于它要求的是方案数,而选择的
b
i
b_{i}
bi又要求是递增的,我们需要将选择状态,上一个点,总的已选择题数都加入状态,所以我们得到的是一个
d
p
S
,
i
,
j
,
k
dp_{S,i,j,k}
dpS,i,j,k的4维dp,最后答案就是
d
p
2
n
−
1
,
n
,
0
−
m
,
m
dp_{2^n-1,n,0-m,m}
dp2n−1,n,0−m,m。
再加上需要转移的点于当前选择题数,我们成功将时间复杂度搞到了
O
(
2
n
n
2
m
3
)
O\left(2^nn^2m^3\right)
O(2nn2m3),这还不如暴力了吧。
由于题目要求的是排名顺序种类数,我们没必要让当前点将所有能取的取值都取一遍,只要总的题目数是不超过
m
m
m的,我们就一定有一种方法可以让当前排名顺序合法。
所以,我们从
i
i
i转移到
j
j
j时,只需要加上
j
j
j所需的最小取值,时间复杂度变成了
O
(
2
n
n
2
m
2
)
O\left(2^nn^2m^2\right)
O(2nn2m2),好像还是不如暴力。
我们发现,由于
b
i
b_{i}
bi是不降的,所以当前面的加上
b
i
b_{i}
bi后,后面的也相当于同时加上了
b
i
b_{i}
bi,此时它们之间的差值是不变的,我们可以用
Δ
=
b
j
−
b
i
\Delta=b_{j}-b_{i}
Δ=bj−bi来表示状态。
我们在转移时先将
b
i
b_{i}
bi对于每个还未加的数都先加上,将总数加上
(
n
−
b
i
t
S
)
b
i
(n-bit_{S})b_{i}
(n−bitS)bi后
Δ
\Delta
Δ的限制就与单个点的选值无关了,至于总值有关。
这样,我们就可以将原来的四维dp变成
d
p
S
,
i
,
j
dp_{S,i,j}
dpS,i,j的三维,时间复杂度也降成了
O
(
2
n
n
2
m
)
O\left(2^nn^2m\right)
O(2nn2m),可以过了。虽然看起来有一点卡常,但这题数据好没有梯度呀
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<time.h>
using namespace std;
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
int n,m,bit[(1<<13)+5],a[15],dif[15][15],k,maxx,lim;
LL dp[(1<<13)+5][15][505],ans;
signed main(){
read(n);read(m);for(reg int i=1;i<=n;++i)read(a[i]);lim=(1<<n)-1;
for(reg int i=1;i<=lim;++i)bit[i]=bit[i>>1]+(i&1);
for(reg int i=1;i<=n;++i)if(a[i]>maxx)maxx=a[i],k=i;
for(reg int i=1;i<=n;++i)for(int j=1;j<=n;++j)dif[i][j]=max(0,a[j]-a[i]+(i>j));
for(reg int i=1;i<=n;++i)if(dif[i][k]*n<=m)dp[1<<i-1][i][dif[i][k]*n]=1;
for(reg int i=1;i<=lim;++i)
for(reg int j=1;j<=n;++j)
if(i&(1<<j-1))
for(reg int l=0;l<=m;++l){
if(!dp[i][j][l])continue;
for(reg int k=1;k<=n;++k){
if(((1<<k-1)&i)||dif[k][j]*(n-bit[i])+l>m)continue;
dp[i|(1<<k-1)][k][dif[k][j]*(n-bit[i])+l]+=dp[i][j][l];
}
}
for(reg int i=1;i<=n;++i)for(reg int j=0;j<=m;++j)ans+=dp[lim][i][j];
printf("%lld\n",ans);
return 0;
}