2026年5月22日讲题题单

D - 帽子 Šeširi

神秘构造题,我们考虑最后的帽子集合是怎么样情况的时候第 \(i\) 个人无法分辨自己的帽子,发现当且仅当他看到别人的帽子相同的时候。
于是我们想到,可以建立双射。 集合 \(S\) 和 集合 \(S \cup {i}\) 之间建立双射,发现这两种情况只能满足一个猜对,所以我们就把这两个点连接一条无向边。
我们对这个图跑一个欧拉回路按照我们跑出来的回路的方向定向每条边,永远满足出边的那个集合可以猜对就好。
(trick:如果有一些点的度数为奇数,我们可以把这个点连接向一个边到虚拟点 n+1,这样可以满足所有点的度数为偶数)。

这个题目根据写法不同会需要二进制反转,太毒瘤了!

E - Permutation Counting 2

直接计数很困难,考虑转化这个问题。其实个人觉得本质就是如何在一个好用的形式来描述这个问题。
首先我们发现直接算 \(k\) 个上升段不好算,于是我们进行容斥,改成钦定 \(k\) 个上升段。
这题,我们对于每一个连续段考虑整体转移。如果我们按照下标从小到大填了 \(p\) 每一个连续段,那么也就相当于每次在 \(p^{-1}\) 中填上了前几小的数字(而且这几个数组的下标就是我在 \(p\) 中的值)。
不难发现,我们用一个矩阵 \(a_{i,j}\) 表示在填 \(p\) 的第 \(i\) 个连续段的同时,\(p^{-1}\) 中第 \(j\) 个连续段多填了多少个。
这样我们就很好计算了,我们只要满足一下条件即可。

  • 每一行不全部为 \(0\)
  • 每一列不全部为 \(0\)
  • 矩阵内所有数字的和为 \(n\)

这个我们需要再来一次容斥,把前两个条件先去掉,最后再用二维二项式反演回去就行。
一共要反演 \(4\) 次。

P - Quantifier

巨大的推式子题,先考虑每次把所有芯片尽量下放,然后再向上归并。
\(f_{u,i,j}\) 表示在节点 \(u\),颜色为 \(i\) 的芯片开头,连续开头长度为 \(j\)
式子太长了,不如放一下关键函数。

void merge(int g[2][N], int f[2][N], int m, int n) { // g 的长度为 m,f 的长度为 n 合并这两个兄弟
	// 清空

	sg[0][m + 1] = sg[1][m + 1] = 0;
	for (int i = m; i >= 1; i--) {
		sg[0][i] = (sg[0][i + 1] + g[0][i]);
		sg[1][i] = (sg[1][i + 1] + g[1][i]);
	}
	sf[0][n + 1] = sf[1][n + 1] = 0;
	for (int i = n; i >= 1; i--) {
		sf[0][i] = (sf[0][i + 1] + f[0][i]);
		sf[1][i] = (sf[1][i + 1] + f[1][i]);
	}
  
    // 省略取 mod
    // 下面每个 for 中第一行为颜色相同的开头合并,其他为不同颜色合并
	for (int i = 0; i <= 1; i++) {
		for (int k = 0; k < m; k++) {
			for (int t = 1; t <= n; t++) {
				add(tmp[i][k + t], (ll)sg[i][k + 1] * f[i][t] * C[k + t][t] * C[m - k + n - t - 1][m - k]);
				if (k) add(tmp[i][k], (ll)sg[i][k + 1] * f[i ^ 1][t] * C[m - k + n - 1][m - k]);
			}
		}
	}
	for (int i = 0; i <= 1; i++) {
		for (int k = 0; k < n; k++) {
			for (int j = 1; j <= m; j++) {
				add(tmp[i][k + j], (ll)g[i][j] * sf[i][k + 1] * C[k + j][j] * C[m - j - 1 + n - k][n - k]);
				if (k) add(tmp[i][k], (ll)g[i ^ 1][j] * sf[i][k + 1] * C[m - 1 + n - k][n - k]);
			}
		}
	}
	for (int i = 0; i <= 1; i++) {
		for (int j = 1; j <= m; j++) {
			for (int t = 1; t <= n; t++) {
				add(tmp[i][j + t], (ll)g[i][j] * f[i][t] * C[j + t][t] * C[m - j + n - t][m - j]);
				add(tmp[i][j], (ll)g[i][j] * f[i ^ 1][t] * C[m - j + n][n]);
				add(tmp[i][t], (ll)g[i ^ 1][j] * f[i][t] * C[m + n - t][m]);
			}
		}
	}

	for (int j = 0; j <= 1; j++) {
		for (int k = 0; k <= n + m; k++) {
			g[j][k] = tmp[j][k];
		}
	}
}
posted @ 2026-05-26 12:22  suuugg  阅读(10)  评论(0)    收藏  举报