Cycle
以下把将排列 进行 次 操作记为 ,那么将排列 进行 次 就是 。
- [I] 首先我想到了一个思路:求出答案的逆排列 ,然后计算 是否为 。但是我肯定是求不出答案的逆排列的,所以这个做法写不了。
然后我会了 的做法。首先一个 的搜排列是很简单的,但是判断是否合法需要点功夫。我发现我定义的乘方运算满足结合律,因此可以进行排列快速幂。时间复杂度 ,可以通过 分。
#include <iostream>
#include <numeric>
using namespace std;
const int kMaxN = 1e5 + 5;
int a[kMaxN], b[kMaxN], f[kMaxN], res[kMaxN], tmp[kMaxN], ans[kMaxN], c[kMaxN], n, k, has;
void times(int res[], int other[]) {
copy(other + 1, other + n + 1, tmp + 1);
for (int i = 1; i <= n; i++) {
res[i] = tmp[res[i]];
}
}
void pow(int k) {
copy(a + 1, a + n + 1, b + 1);
iota(res + 1, res + n + 1, 1);
for (; k; k /= 2) {
if (k % 2) {
times(res, b);
}
times(b, b);
}
}
bool check() {
pow(k);
return equal(res + 1, res + n + 1, ans + 1);
}
void dfs(int x) {
if (has) {
return;
}
if (x > n) {
if (check()) {
has = 1;
copy(a + 1, a + n + 1, c + 1);
}
return;
}
for (int i = 1; i <= n; i++) {
if (f[i]) {
continue;
}
f[i] = 1;
a[x] = i;
dfs(x + 1);
f[i] = 0;
}
}
int main() {
cin.tie(0)->sync_with_stdio(0);
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> ans[i];
}
dfs(1);
for (int i = 1; i <= n; i++) {
cout << c[i] << ' ';
}
return 0;
}接下来我就思考排列的性质。一个排列可以拆成若干个不相干的环( 往 连边),而我发现将排列进行 次方运算之后,原排列中一个长度为 的环会变成 个长度为 的小环。
而这道题需要我们还原最开始的排列,因此是合并环。假设在给出的排列中找到了若干个长度为 的小环,这些小环原来是长度为 的大环,那么满足
要找出最小的 来方便处理。然后我就不会了。
Coloring
首先我先 枚举出每个点的颜色,然后对于每条边染成黑色是否满足条件就可以确定下来了,因此不要实际染边的颜色。把所有满足条件的边的数量记下来,假设是 ,那么枚举实际染成的黑色边数量 ,并通过组合数计算最终答案。
成功获得 分。
Disbalance
- [c] 我的暴力挂了一分都没有。
大概就是每次枚举给哪个数加一,太暴力了。
根据讲题和 AI 分析我整理出了 的解法。
- [I] 进行若干次操作之后,不同的最终状态是等概率的。不会证哈哈。
对于 个元素,如果我们需要将 次增量随机赋给这些元素的话,那么这就和可重组合数一样了(在 个元素中选择 个可重元素的组合)。根据插板法,最终状态的总数就是 。
由于所有最终状态等概率,因此每种状态的概率为 。
不平衡值的定义是对于 ,如果 ,那么值就为 ,否则为 。
- [I] 由于元素之间地位是相等的,也没有顺序之分,因此我们可以求出其中一个数作为 的贡献,然后将这个贡献乘以 。
假设 最终是 ,那么我们可以枚举最终给 的增量 ,然后把剩余的 个增量给到其它 个元素上,方案就为
再乘上每种状态的概率,对于这种情况概率就是
把枚举 的过程显式地加入尽量,并加上对答案贡献的计算,可以得到 的值
将 变形得到 ,因此下界为 。再把常数项移出去得到
其中 。
直接暴力计算时间复杂度 ,需要化简求和部分。
- [n] 先进行换元,令 ,那么 ,由于 ,所以 。
带入原式得到
把求和拆开,就有
- [!] 注意到 是固定的,也就是我们所有求的组合数在杨辉三角上是同一列,而杨辉三角一列的和等于最后一个元素的右下角那个元素,也就是 1。对第一个求和应用恒等式得到
对于第二个求和式,首先利用恒等式 的变形 。若 ,那么 。
所以
再次应用 恒等式,得到最终结果
所以我们就得到了 的最终结果
- [!] 需要注意当 时,贡献为 。
预处理阶乘和逆元后,以上公式可以 进行计算。所以我们就得到了本题的 解法。
Distance
- [I] 以不同点为根的贡献是可以拆开进行计算的。
所以我们枚举根,然后做倍增 LCA。预处理的时候把树上前缀和的搞下来,但是这样子就无法快速子树修改了。由于 dfs 序可以做字数修改,因此按照 dfs 序把树上前缀和差分后插入到树状数组中,这样子就可以快速区间修改单点查询了。
时间复杂度 ,但是常数太大只有 分。