专栏文章
题解:P7213 [JOISC 2020] 最古の遺跡 3
P7213题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @min5uaur
- 此快照首次捕获于
- 2025/12/01 21:03 3 个月前
- 此快照最后确认于
- 2025/12/01 21:03 3 个月前
我计数咋这么菜。
题目链接
解题思路 & 参考代码
贡献延后转移 trick。
首先我们容易注意到对于一个合法的初始石柱状态,最终一定只会剩下 中的数,且每个数恰好出现一次。
具体证明就是,初始 所有数出现 次,显然的,对于一个数 若出现了两次,则一次操作会将其中一个 变为 ,若 也有两个数,则其中一个 会变为 ,因此,一次操作后,所有的数字出现个数仍然 ,该结论成立。
考虑刻画这个形式,容易发现为遍历 ,把最后一个 且之前未被标记过的数标记,最终被标记的位置就为原序列的最终石柱位置。
但是这东西不好直接做,考虑直接对着原序列 进行刻画,可以发现我们可以直接遍历 ,同时在遍历过程中维护一个集合 ,如果此时 中包含了 ,那么 不能被加入 内,否则我们可以往 内加入一个最大的 的不在 中的数,设其为 (若没有则 ),此时 为其中一个最终石柱位置。
容易注意到这两个东西事实上是等价的,所以这样是对的。
刻画完后,我们可以考虑 dp。
为了方便,我们在下文转移时钦定所有数均是不同的,最后将答案乘上 即可。
假设从后往前第 个位置之前(不含第 个位置)已经有 个位置不为最终石柱位置, 个位置为最终石柱位置。
设 表示考虑了后 个数, 中含有 且不含有 的方案数,考虑如何转移 :
- 如果我们钦定第 个位置不为最终石柱位置,那么权值可以从 中选取,由于之前最终石柱位置选取了 个,不在最终石柱位置的位置有 个,那么方案数就为 ,有转移:
- 如果我们钦定第 个位置为最终石柱位置,可以进行以下分讨:
- 若 ,考虑延后转移,即:
- 否则有 ,那么此时 会改变,枚举 变为了 ,那么我们需要把延迟转移的 拿来填 :
- 显而易见的,决定延迟转移的柱子数量为 ,选其中的 ,为 。
- 然后我们需要考虑 必须要满足 ,有 种方案。
- 这里我们需要设一个 表示有 根柱子,操作后高度 连续的方案数,因此我们需要带一个 的系数,有转移:
考虑如何转移 ,可以枚举第一根柱子的最终高度 ,然后之后的 根柱子需要选取 个位置,方案数即为 ,同时容易注意到 与 的部分是相互独立的,所以可以直接乘上 ,考虑 的方案数, 初始有 种,由于有 种已经被选过了,因此还剩下 种,因此有转移:
。
于是就做完了,时间复杂度 。
参考代码
CPPll n,m;
ll vis[1210],f[1210][1210],g[1210];
ll s0,s1;
void solve()
{
cin>>n;
forl(i,1,n)
vis[rd()]=1;
m=n*2;
g[0]=1;
forl(x,1,m)
forl(i,1,m)
add(g[x],g[i-1]*g[x-i]%mod*C(x-1,i-1)%mod*(x-i+2)%mod);
f[m+1][0]=1;
forr(i,m,1)
{
if(vis[i])
{
forl(j,0,s1)
{
add(f[i][j],f[i+1][j]);
forl(k,j+1,s1+1)
add(f[i][k],f[i+1][j]*C(s1-j,k-j-1)%mod*(k-j+1)%mod*g[k-j-1]%mod);
}
s1++;
}
else
{
forl(j,s0,n)
add(f[i][j],(j-s0)*f[i+1][j]%mod);
s0++;
}
}
cout<<pw(pw(2,n,mod),mod-2,mod)*f[1][n]%mod<<endl;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...