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

树形dp 点分治

首先我先 枚举出每个点的颜色,然后对于每条边染成黑色是否满足条件就可以确定下来了,因此不要实际染边的颜色。把所有满足条件的边的数量记下来,假设是 ,那么枚举实际染成的黑色边数量 ,并通过组合数计算最终答案。

成功获得 分。

Disbalance

概率与期望 组合数学 乘法逆元

  • [c] 我的暴力挂了一分都没有。

大概就是每次枚举给哪个数加一,太暴力了。


根据讲题和 AI 分析我整理出了 的解法。

  • [I] 进行若干次操作之后,不同的最终状态是等概率的。不会证哈哈。

对于 个元素,如果我们需要将 次增量随机赋给这些元素的话,那么这就和可重组合数一样了(在 个元素中选择 个可重元素的组合)。根据插板法,最终状态的总数就是

由于所有最终状态等概率,因此每种状态的概率为

不平衡值的定义是对于 ,如果 ,那么值就为 ,否则为

  • [I] 由于元素之间地位是相等的,也没有顺序之分,因此我们可以求出其中一个数作为 的贡献,然后将这个贡献乘以

假设 最终是 ,那么我们可以枚举最终给 的增量 ,然后把剩余的 个增量给到其它 个元素上,方案就为

再乘上每种状态的概率,对于这种情况概率就是

把枚举 的过程显式地加入尽量,并加上对答案贡献的计算,可以得到 的值

变形得到 ,因此下界为 。再把常数项移出去得到

其中

直接暴力计算时间复杂度 ,需要化简求和部分。

  • [n] 先进行换元,令 ,那么 ,由于 ,所以

带入原式得到

把求和拆开,就有

  • [!] 注意到 是固定的,也就是我们所有求的组合数在杨辉三角上是同一列,而杨辉三角一列的和等于最后一个元素的右下角那个元素,也就是 1。对第一个求和应用恒等式得到

对于第二个求和式,首先利用恒等式 的变形 。若 ,那么

所以

再次应用 恒等式,得到最终结果

所以我们就得到了 的最终结果

  • [!] 需要注意当 时,贡献为

预处理阶乘和逆元后,以上公式可以 进行计算。所以我们就得到了本题的 解法。

Distance

树链剖分 线段树 矩阵

  • [I] 以不同点为根的贡献是可以拆开进行计算的。

所以我们枚举根,然后做倍增 LCA。预处理的时候把树上前缀和的搞下来,但是这样子就无法快速子树修改了。由于 dfs 序可以做字数修改,因此按照 dfs 序把树上前缀和差分后插入到树状数组中,这样子就可以快速区间修改单点查询了。

时间复杂度 ,但是常数太大只有 分。

Footnotes

  1. 朱世杰恒等式 - 维基百科,自由的百科全书