题解:P5215 [SHOI2014] 神秘金字塔
思路
首先发现
设第
那么就可以 DP 了,设
考虑从
时间复杂度
细节
首先为了记录状态,我们需要对每一个
预处理
对于高维后缀和,我们需要预处理一个
常数小的原因是长度固定时,阶梯的编号是一个区间,内存访问较为连续,所以可以通过。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 7e4+5,mod = 1e9+7;
int cnt;
map<vector<int>,int> mp;
vector<int> now;
int lim,lt[N],rt[N],del[30][N],mn[30][N],sum[N];
void dfs(int x)
{
if(x==lim)
{
mp[now] = ++cnt;
mn[lim][cnt] = cnt;
for(auto i:now) sum[cnt]+=i;
if(!lt[lim]) lt[lim] = cnt;
rt[lim] = cnt;
for(int i = 1;i<lim;i++)
{
vector<int> tmp;
for(int j = 0;j<i;j++)
tmp.push_back(min(now[j],i));
mn[i][cnt] = mp[tmp];
}
for(int i = 1;i<lim;i++)
{
if(now[i]>(i==lim-1?1:now[i+1]))
{
now[i]--;
del[i][cnt] = mp[now];
now[i]++;
}
}
return;
}
for(int i = 1;i<=now[x-1];i++)
{
now[x] = i;
dfs(x+1);
}
}
vector<int> vec[N];
int n,h,a[N],f[2][255][N];
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
for(lim = 1;lim<=10;lim++)
{
now.resize(lim);
now[0] = lim;
dfs(1);
}
cin>>n>>h;
if(n%4) return cout<<0,0;
n/=4;
for(int i = 1,x;i<=h;i++)
{
cin>>a[i];
if(a[i]%2) return cout<<0,0;
a[i]/=2;
if(i==1)
{
for(int j = lt[a[i]];j<=rt[a[i]];j++)
if(sum[j]<=n) f[1][sum[j]][j] = 1;
}
else
{
int now = (i&1);
for(int j = 0;j<=n;j++)
for(int k = lt[a[i]];k<=rt[a[i]];k++)
f[now][j][k] = 0;
for(int j = 0;j<=n;j++)
for(int k = lt[a[i-1]];k<=rt[a[i-1]];k++)
(f[now][j][mn[a[i]][k]]+=f[now^1][j][k])%=mod;
for(int j = n;j;j--)
{
for(int x = a[i]-1;x;x--)
for(int k = rt[a[i]];k>=lt[a[i]];k--)
(f[now][j][del[x][k]]+=f[now][j][k])%=mod;
for(int k = lt[a[i]];k<=rt[a[i]];k++)
{
if(j+sum[k]<=n) (f[now][j+sum[k]][k]+=f[now][j][k])%=mod;
f[now][j][k] = 0;
}
}
}
}
int ans = 0;
for(int i = lt[a[h]];i<=rt[a[h]];i++)
(ans+=f[h&1][n][i])%=mod;
cout<<ans;
return 0;
}