P8180 「EZEC-11」Unmemorable 题解
题目传送器
更爽的阅读体验
前言
赛时会了,结果预处理忘优化了,大样例跑得飞快, 假完甲烷了,喜提暴力分。
题意
自己看题面。
做法
先大眼暴力,观察结论(我也不会证明,但把表打出来后还比较明显):
- 数组无用
- 顺序正确的 数组唯一
- 先从大到小排序,对于当前值 ,放入第一个 的位置,。形成最后的 。
对于第 点的观察,可以自己参考别的题解的做法,反正能确定 数组即可。
现在已经得到了 数组了, 数组已经固定,有保证有解,所以算出多少个排列 满足 的限制即可。
其实做到这一步,就完全是这题了,接下来讲一讲具体的做法。
对于当前数组能推出的信息很少,所以先从局部推结论。不难发现在当前 数组中 最小的位置一定是当前最后一个 的 ,证明可以设 ,因为是最小值,所以前面不会再有 ,故 一定是 ,最后一个 一定会满足前面的 。
推出当前序列的最小值后,序列就又分成了两个独立的区间,因为保证有解,所以 ,将 的 后,后面的部分就与前面独立了。接下来就类似于分治的做法,将当前区间分成两段,递归求解。设当前区间为 ,这个区间的最小值已经确定,从剩下的 个数中选取 个数作为左边区间,剩下就是右边区间,所以要再乘上 。形式化的,设 为区间 的分配方案数(数的集合已经固定,相当于离散化后的方案数)。
想到这里就完成了,实现并不难。注意 是区间最小值,但并不一定是 ,所以直接做不是 的,而是卡不满的 。可以使用 st 表或线段树做 rmq。本题空间要求线性,于是只能使用线段树,复杂度为 。
Code
递归求 的主要代码如下:
void dfs(int l,int r){
if(l>r) return;
int x=-query(all,l,r).se; // 区间最小值的位置
mmul(ans,C(r-l,x-l));
dfs(l,x-1);
dfs(x+1,r);
}