题解:P7519
来一个 naive 的做法。
首先考虑
具体的,我们发现,如果
我们发现,除了第一个人以外,其他所有人只要满足比前一个人高即可,第一个人要比其他所有人都高,于是可以做到
我们发现,
这提示我们,可以直接上 Meet-in-Middle,枚举后一半,再枚举前一半合并,假设前一半的最后一个
暴力前缀和的总复杂度是
如果你用树状数组维护前缀和可以做到 但
#include<bits/stdc++.h>
using namespace std;
int read(){
int t=0;char v=getchar();
while(v<'0')v=getchar();
while(v>='0')t=(t<<3)+(t<<1)+v-48,v=getchar();
return t;
}
int n,p[15],a[15],m,b[15],LMT,f[8195][15][502];
bool v[15];
long long ans;
void dfs(int x,int y){
if(x==LMT+1){
int zt=0;
for(int i=1;i<x;++i)zt|=(1<<p[i]-1);
++f[zt][p[1]][y];
return;
}
int mx=-1;
for(int i=1;i<=n;++i)
if(!v[i]){
int dlt=a[p[x-1]]+b[x-1]-a[i];
if(p[x-1]<i)++dlt;
mx=max(mx,a[i]);
if(dlt<b[x-1])dlt=b[x-1];
if(y+dlt>m)continue;
b[x]=dlt,p[x]=i,v[i]=1,dfs(x+1,y+dlt),v[i]=0;
}
}
void dfs1(int x,int y){
if(x==LMT+1){
int zt=0,B=(1<<n)-1;
for(int i=1;i<x;++i)zt|=(1<<p[i]-1);
B^=zt;
for(int i=1;i<=n;++i)
if(B&(1<<(i-1))){
int dlt=a[p[x-1]]+b[x-1]-a[i];
if(p[x-1]<i)++dlt;
if(dlt<b[x-1])dlt=b[x-1];
dlt=max(dlt,0);
int ss=dlt*(n-LMT)+y;
if(ss<=m)ans+=f[B][i][m-ss];
}
return;
}
int mx=-1;
for(int i=1;i<=n;++i)
if(!v[i]){
int dlt=a[p[x-1]]+b[x-1]-a[i];
if(p[x-1]<i)++dlt;
if(x==1){dlt=max(dlt,mx-a[i]+1);for(int j=i+1;j<=n;++j)if(!v[j])dlt=max(dlt,a[j]-a[i]);}
mx=max(mx,a[i]);
if(dlt<b[x-1])dlt=b[x-1];
if(y+dlt>m)continue;
b[x]=dlt,p[x]=i,v[i]=1,dfs1(x+1,y+dlt),v[i]=0;
}
}
int main(){
n=read(),m=read(),a[0]=-1e9;
for(int i=1;i<=n;++i)a[i]=read(),p[i]=i;
LMT=n>>1;
dfs(1,0);
for(int i=0;i<(1<<n);++i)for(int j=1;j<=n;++j)for(int k=1;k<=m;++k)f[i][j][k]+=f[i][j][k-1];
LMT=(n+1)>>1;
dfs1(1,0);
printf("%lld",ans);
}