1 条题解
-
0
自动搬运
来自洛谷,原作者为

ddh123
虚拟正是人类的本质,人们什么时候能看到最澄澈的空与海呢搬运于
2025-08-24 23:05:22,当前版本为作者最后更新于2025-01-11 01:18:13,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这是本蒟蒻的第一篇正式题解。本题重点在于找到无解序列的充要条件,首先记 为游戏结束时第 个数取 的对数, 为第 个数出现时间离散化后的值,显然 相邻两项不同, 是一个排列,如果一个位置比它相邻位置大且出现时间小,直接按照顺序合成即可,如果一个元素比它相邻位置小且出现时间小呢?我们可以先合成较大数的一半再合成较小数,最后合成较大数,具体参考样例解释中 的第四种情况。
一些性质
-
是单峰的, 是单谷的,归纳易证。
-
,这是因为已经有 个位置被占,且第个数至少需要 个位置才能合成。
-
若 ,那么 ,这是因为如果 ,那么 的合成会影响到 的合成。同理,若 ,那么 。
-
若 ,那么不可能会有 ,这是因为如果 ,那么首先要合成 ,然后合成 ,最后合成 ,这会使 无法进一步合成出 。同理,若 ,那么不可能会有 。
我们容易发现,这些性质已经充要,我们来考虑如何 dp。
DP
考虑到性质 1 和性质 4,可以发现出现时间最小的位置只能是 中最大的位置或这个位置的相邻两个位置,也就是下图中红框框住的点。也就是说出现时间最小的位置的两边元素大小分别是单增和单减,因此我们可以按照出现时间从大到小进行转移。

设 表示已经有 个数确定,其中左块最右边的数是 ,右块最左边的数是 ,首先考虑 与 的范围。假设格子总数是 ,第 个转移的数大小为 ,根据性质 2 有:,也就是 ,因此有 ,。转移方程为:$F_{i,L,R}=(\sum_{[tl\lt L]}F_{i-1,tl,R} )+(\sum_{[tr\lt R]}F_{i-1,L,tr} )$,这个东西可以用前缀和优化,贡献答案时差分即可,时间复杂度 。
AC Code
#include<bits/stdc++.h> using namespace std; int T,mod,n,x,dp[305][305][305],f[305][305][305],g[305][305][305],sum[305][305]; void add(int &x,int y){ (x+=y)%=mod; } void fun(int n,int lx,int rx,int res){//计算贡献 add(sum[n][lx],res),add(sum[n][rx+1],mod-res); } int main(){ scanf("%d%d",&T,&mod); dp[0][0][0]=g[0][0][0]=f[0][0][0]=1,fun(1,1,1,1); for(int i=1;i<300;i++) for(int j=0;j<=i;j++) for(int k=0;k<=i;k++)if(j+k){ if(j)add(dp[i][j][k],f[i-1][j-1][k]); if(k)add(dp[i][j][k],g[i-1][j][k-1]); f[i][j][k]=g[i][j][k]=dp[i][j][k],fun(i+1,max(j,k)+1,i+1,dp[i][j][k]); if(j+1<=k-2)fun(i+1,k,k,dp[i][j][k]*2LL*((k-2)-(j+1)+1)%mod); //考虑到对称性直接*2即可 if(j)add(f[i][j][k],f[i][j-1][k]); if(k)add(g[i][j][k],g[i][j][k-1]); } for(int i=1;i<=300;i++)for(int j=1;j<=300;j++)add(sum[i][j],sum[i][j-1]); for(int i=1;i<=300;i++)for(int j=1;j<=300;j++)add(sum[i][j],sum[i][j-1]); while(T--)scanf("%d%d",&n,&x),printf("%d\n",sum[n][x-1]); return 0; }如有表达不清可以在评论区里交流讨论。
-
- 1
信息
- ID
- 10550
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者