专栏文章
题解:P5215 [SHOI2014] 神秘金字塔
P5215题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @min4344l
- 此快照首次捕获于
- 2025/12/01 20:14 3 个月前
- 此快照最后确认于
- 2025/12/01 20:14 3 个月前
思路
首先发现 一定为偶数,且左右上下对称,所以可以直接分成四半大小为 的部分,每层的最大宽度减半。
设第 列有 个数,根据题目限制,需要满足 ,且 。这个东西的个数并不多,具体的,当 时,状态数等于 。
那么就可以 DP 了,设 表示第 层,用了 个方块,这一层的阶梯为 的方案数。
考虑从 转移到 ,设 为最大一个阶梯使得长度为 且被 包含。先把 转移到 ,然后再对每一个 做高维后缀和即可。最后处理每个阶梯对 的影响。
时间复杂度 ,其中 表示不同的阶梯数量,最大为 。算出来大约为 ,不过常数被实现得特别优秀,可以通过。
细节
首先为了记录状态,我们需要对每一个 按字典序从小到大枚举阶梯,将每个阶梯映射到一个编号,这一步使用 map 即可。
预处理 表示最大一个阶梯使得长度为 且被 包含。这个是简单的, 的第 个位置等于 ,套用上面的编号即可。
对于高维后缀和,我们需要预处理一个 表示 将第 位减一后到哪个区间。做高维后缀和的时候,直接从大到小枚举 ,从大到小枚举 的编号,让 加上 即可。
常数小的原因是长度固定时,阶梯的编号是一个区间,内存访问较为连续,所以可以通过。
代码
CPP#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;
}
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...