毒题选讲选做

董先生的休闲方案

\(\texttt{source: superguymj}\)

题意

\(n\) 张牌,每次随机选一张,如果选择的牌是当前没翻开的最小值,那么就翻开,否则合上并且记住这张牌是什么,问最后期望翻开的次数。

记期望耗时为 \(\displaystyle \frac u v\) ,其中 \(u, v\) 互素。若存在整数 \(v^{-1}\) ,满足 \(vv^{-1} \equiv 1 \pmod {p^k}\) ,则模 \(p^k\) 意义下的答案即为 \(uv^{-1} \pmod {p^k}\)

对于 \(100\%\) 的数据,保证存在整数 \(v^{-1}\)\(p\) 为奇素数,且 \(p \le 10^5, np^k \le 10^{18}\)

题解

\(f_i\) 表示剩余 \(i\) 张牌未翻开的期望耗时,显然有

\[f_i = \frac 1 i(f_{i - 1} + 1) + (1 - \frac 1 i)(f_{i - 1} + 2) = f_{i - 1} + 2 - \frac 1 i \]

答案就是

\[f_n = 2n - \sum_{i = 1}^{n} \frac 1 i \]

问题就变成如何求

\[F(n, p, k) \equiv \sum_{i = 1}^{n} \frac 1 i \pmod {p^k} \]

经典的调和级数,有什么高妙的推法呢?

\[\begin{aligned} F(n, p, k) &\equiv \sum_{i = 1}^n \frac{[p \nmid i]}{i} + \sum_{i = 1}^{\lfloor \frac n p \rfloor} \frac 1{ip} \pmod {p^k}\\ &\equiv \sum_{i = 1}^n \frac{[p \nmid i]}{i} + \frac {F(\lfloor \frac n p \rfloor, p, k + 1)}{p} \pmod {p^k} \end{aligned} \]

注意虽然 \(\displaystyle \frac 1 p \bmod {p^k}\) 无定义,但是加起来 \(ap\) 个就可能存在定义。那么我们后面的地方要升上去一个 \(p\) 的幂。

直接实现是复杂度是 \(\mathcal O(n \log n)\) 的,考虑把前面那个式子进行变换。

\[\begin{aligned} f(n, p, k) &\equiv \sum_{i = 1}^n \frac{[p \nmid i]}{i} \pmod {p^k}\\ &= \sum_{i = 1}^{p - 1} \sum_{j = 0}^{\lfloor \frac n p \rfloor - 1} \frac 1{jp + i} + \sum_{i = \lfloor \frac n p \rfloor + 1}^{n} \frac 1 i \pmod {p^k} \end{aligned} \]

后面那部分长度是 \(n \bmod p < p\) ,利用离线逆元即可线性处理。

然后我们考虑用广义二项式定理展开 \((jp + i)^{-1}\) ,注意到 \(\displaystyle {-1 \choose x} = (-1)^{x}\)

\[\sum_{i = 1}^{p - 1} \sum_{j = 0}^{\lfloor \frac n p \rfloor - 1} \frac 1{jp + i} \equiv \sum_{i = 1}^{p - 1} \sum_{j = 0}^{\lfloor \frac n p \rfloor - 1} \sum_{x = 0}^{\infty} \frac {(-1)^x (jp)^x}{i^{x + 1}} \pmod {p^k} \]

还要注意到 \(x \ge k\)\(p^x \bmod p^k\)\(0\) ,顺便交换一下和式

\[\sum_{i = 1}^{p - 1} \sum_{j = 0}^{\lfloor \frac n p \rfloor - 1} \sum_{x = 0}^{k - 1} \frac {(-1)^x (jp)^x}{i^{x + 1}} \equiv \sum_{x = 0}^{k - 1} \sum_{i = 1}^{p - 1} \frac {(-1)^x p^x}{i^{x + 1}} \sum_{j = 0}^{\lfloor \frac n p \rfloor - 1} j^x \pmod {p^k} \]

由于模数鬼畜,我们考虑用矩阵乘法求自然数幂和,要基于

\[i^k = (i - 1 + 1)^k = \sum_{j = 0}^{k} {k \choose j} (i - 1)^j \]

这个组合递推式,可以预处理然后做到 \(\mathcal O(k^3 \log n)\)

那么最后的复杂度就是 \(\mathcal O((pk + k^3 \log n) \log n)\) 的。

其实 \(\texttt{min_25}\) 博客 里有个 \(\mathcal O(\sqrt p \log p)\) 求调和级数模 \(p\) 下的做法qwq

Mr. Kitayuta's Gift

\(\texttt{source: CF506E}\)

题意

给一个仅包含小写字母的字符串 \(s\) 。你需要选择恰好 \(n\) 个小写字母,并把他们分别插入到 \(s\) 的任意位置。问通过这种方式能生成出多少种回文串。

数据范围

\(|s| \le 200, n \le 10^9\)

题解

题目等价于,问有多少个长为 \(|s| + n\) 本质不同的回文串 \(t\) ,满足 \(s\) 为它的一个子序列。

首先不难想到一个很暴力的 \(dp\) ,令 \(dp(i, l, r)\) 为考虑完 \(t\) 的前后 \(i\) 个字符,然后还没有匹配上 \(s_l \sim s_r\) 这一段。

转移的时候分 \(s_l = s_r\)\(s_l \not = s_r\) 讨论,复杂度就是 \(\mathcal O(|s|^2 n)\) 的。

然后发现其实是个常系数线性递推,用矩阵快速幂优化到 \(\mathcal O(|s|^6 \log n)\)

接下来的部分内容和 ,可以参考一下这篇 博客

然后考虑优化一下,不难发现其实我们就是问有多少个 \(t\) 的满足,能完全匹配上 \(s\) 这个特殊的序列自动机。

我们可以把 \(s_l \not = s_r\) 的称为 \(n24\) 类节点,\(s_l = s_r\) 的称为 \(n25\) 类节点。因为我们可以发现 \(n24\) 类节点有 \(24\) 个自环,\(n25\) 类节点有 \(25\) 个自环,注意一下结束节点 \(q_0\)\(26\) 个自环,我们称其为 \(n26\) 类节点。

对于一条合法的自动机路径来说,设我们经过的不同的 \(n24\) 类节点个数为 \(a\)\(n25\) 类节点个数为 \(b\) ,那么显然有 \(b = \displaystyle \lceil \frac {|s| - a} 2\rceil\) ,并且 \(n24\)\(n25\) 类节点的先后顺序其实并不会影响我们的答案。

为什么呢?因为这样变化后的转移顺序虽然改变了,但是最后的每个系数的积并没有改变。

然后我们我们考虑设一个辅助 \(dp\)\(f(i, l, r)\) 为当前未匹配的是 \(s_l \sim s_r\) 经过 \(i\)\(n24\) 类节点的方案数。

由于本质不同的 \((a, b)\) 序列只有 \(\mathcal O(|s|)\) 个,那么复杂度就优化成 \(\mathcal O(|s|^4 \log n)\) 了。但仍然还过不去。。。

然后考虑继续优化,我们其实可以把这 \(\mathcal O(|s|)\) 个串的自动机一起建,怎么搞呢?

我们一开始先搞 \(\mathcal O(|s|)\)\(n24\) 类节点顺次相连,然后后面接 \(\mathcal O(|S|)\)\(n25\) 类节点,对于每个 \(n25\) 类节点下面接一个 \(n26\) 类节点。

这样的话,我们就可以做一遍矩阵快速幂,求出任意两个节点之间的方案数了。

然后我们可以枚举一开始经过的 \(n24\) 类点的数量,然后直接算答案就行了。

注意当 \(|t|\) 为奇数的时候会有些诡异,因为中心只有一个字符。考虑把不合法的减掉,就是最后一个中心匹配两个的时候,等价于倒数一步匹配剩下了的两个字符,这个减掉就好啦。

然后如果按拓扑序标号,那么只可能前面向后面连边,那么矩阵快速幂的时候枚举上三角就行了,乘上一个 \(\displaystyle \frac 1 6\) 的常数 233

复杂度就变成了 \(\mathcal O(|s|^3 + |s|^3 \log n)\)

可以考虑用 \(\mathcal {Cayley-Hamilton}\) 优化到 \(\mathcal O(|s|^3 + |s|^2 \log n)\) 。。但是俺不会TAT

题解里还提到了 \(\mathcal O(|s|^3 + \log n)\) 的不需要矩阵的做法,但是是一个艰深的推式子做法,不优美且十分麻烦。。。

代码

#include <bits/stdc++.h>

#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl

using namespace std;

template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }

inline int read() {
	int x(0), sgn(1); char ch(getchar());
	for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
	for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
	return x * sgn;
}

void File() {
#ifdef zjp_shadow
	freopen ("E.in", "r", stdin);
	freopen ("E.out", "w", stdout);
#endif
}

const int N = 210, Mod = 10007;

int len;

struct Mat {

	int a[N << 1][N << 1];

	Mat() { Set(a, 0); }

	void Unit() { For (i, 1, len) a[i][i] = 1; }

	inline Mat friend operator * (const Mat &lhs, const Mat &rhs) {
		Mat res;
		For (i, 1, len) For (j, i, len) {
			long long tmp = 0;
			For (k, i, j) tmp += lhs.a[i][k] * rhs.a[k][j];
			res.a[i][j] = tmp % Mod;
		}
		return res;
	}

};

inline Mat fpm(Mat x, int power) {
	Mat res; res.Unit();
	for (; power; power >>= 1, x = x * x)
		if (power & 1) res = res * x;
	return res;
}

int f[N][N][N]; char S[N];

int Calc(int x, int l, int r) {
	int &val = f[x][l][r];
	if (~val) return val;
	if (l == r) return val = (!x);
	if (S[l] == S[r])
		return val = (l + 1 == r) ? (!x) : Calc(x, l + 1, r - 1);
	return val = (x > 0) ? (Calc(x - 1, l + 1, r) + Calc(x - 1, l, r - 1)) % Mod : 0;
}

int main () {

	File();

	Set(f, -1);
	scanf ("%s", S + 1);
	int m = strlen(S + 1), n = read() + m;

	int l = (n + 1) / 2, n24 = m - 1, n25 = (m + 1) / 2, n26 = n25;
	len = n24 + n25 + n26;

	Mat base;
	For (i, 1, n24)
		base.a[i][i] = 24, base.a[i][i + 1] = 1;
	For (i, n24 + 1, n24 + n25)
		base.a[i][i] = 25, base.a[i][i + 1] = (i < iend), base.a[i][i + n25] = 1;
	For (i, n24 + n25 + 1, len)
		base.a[i][i] = 26;
	Mat powa = fpm(base, l - 1), powb = powa * base;

	int ans = 0;
	For (i, 0, n24) {
		int j = (m - i + 1) / 2, k = l - (i + j);
		if (k >= 0) {
			int coef = Calc(i, 1, m);
			(ans += coef * powb.a[n24 - i + 1][n24 + n25 + j]) %= Mod;
			if ((n & 1) && !((m - i) & 1))
				(ans -= coef * powa.a[n24 - i + 1][n24 + j]) %= Mod;
		}
	}
	printf ("%d\n", (ans + Mod) % Mod);

	return 0;

}

LYZ-Ice Skates

\(\texttt{source: POI2009}\)

题意

滑冰俱乐部初始有 \(1 \dots n\) 号码溜冰鞋各 \(k\) 双,已知 \(x\) 号脚的人可以穿 \(x \dots x + d\) 号码的鞋子。

现在有 \(m\) 次操作,每次两个数 \(r,x\) ,表示 \(r\) 号脚的人来了 \(x\) 个,\(x\) 为负表示离开。对于每次操作,输出溜冰鞋是否足够。

数据范围

\(n, k, m \le 5 \times 10^5, k \le 10^9\)

题解

显然这是一个二分图匹配的模型,直接跑显然是不行的。

考虑使用 \(\mathcal {Hall}\) 定理进行判断,就是存在完美匹配的充要条件就是对于左边任意一个集合 \(S\) 来说,右边的和 \(S\) 内点的连边都要不小于 \(|S|\)

然后我们显然是考虑连边最小的那个集合,不难发现编号尽量连续的时候边会尽量少,也就是我们要对于所有区间 \([l, r]\) 都要满足(记 \(a_i\) 为脚码为 \(i\) 的人的数量):

\[\sum_{i = l}^{r} a_i \le (r - l + 1 + d) k \]

我们移相化为:

\[\sum_{i = l}^{r} (a_i - k) \le dk \]

然后用线段树维护最大子段和就可以判断了。复杂度是 \(\mathcal O(n \log n)\) 的。

posted @ 2019-03-19 21:42  zjp_shadow  阅读(209)  评论(0编辑  收藏  举报