杂题选做

P6323

不会做黄题。水平退化成啥了。

这类与全排列相关的 dp 题。常见的套路是从小到大填数。本题中考虑 \(f_{i,j}\) 表示填了 \(1 \sim i\),逆序对数为 \(j\)

考虑第 \(i\) 个数插在哪里以及对应的贡献。此时序列中的数只有小于 \(i\) 和大于 \(i\) 两种情况。当前已经插了 \(i-1\) 个数,你会发现只有 \(i\) 个位置是需要考虑的,分别对应了造了 \(0 \sim i-1\) 个逆序对。那么这样转移就做到 \(O(n^2c)\) 了。获得 \(64\)

注意到我们每次都在固定 \((i,j)\) 然后去枚举 \(k\),然后这个 \(k\) 的值域是连续的,于是考虑前缀和优化。这样变成 \(O(nc)\)。滚动一下数组就过了。

没滚动数组的代码

P2401

有实力的。

这题与逆序对那题不同的是,那题求逆序对,我们只关心元素的相对顺序。本题中难以直接计算增加的 < 个数,需要分讨。分讨第 \(i\) 个元素插在哪:

  1. 序列最开头。出现 \(i > S_1\),无贡献。
  2. 序列最末尾。出现 \(S_n < i\),贡献加一。
  3. \(S_l < S_r\) 之间。出现 \(S_l < i > S_r\),无贡献。
  4. \(S_l > S_r\) 之间。出现 \(S_l < i > S_r\),贡献加一。

综上,对于一个 \(f_{i,j}\) 表示 \(i\) 个元素造了 \(j\)<

  1. 假设第 \(i\) 个元素多造了一个 <,能插的位置就是 > 的位置加上序列末尾,即 \(i-2-(j-1) + 1\),其中 \(i-2\) 表示符号个数,\(j-1\) 表示 < 个数。那么 \(f_{i,j} = f_{i,j} + f_{i-1,j-1} \times (i-j)\)
  2. 假设没造。能插的位置就是 < 的位置加上序列开头,即 \(j + 1\)\(f_{i,j} = f_{i,j} + f_{i-1,j} \times (j+1)\)

做完了。时间复杂度 \(O(n^2)\)

ABC461E

比较板的 ds 题。

考虑操作 \(1\),横着涂一行黑的会改变什么。如果是第一次涂这一行,贡献显然就是 \(n\)。但是如果不是呢?不妨记上一次涂这一行的时刻为 \(j\),当前时刻为 \(i\)。那么时刻 \(j \sim i\) 中有多少列被涂白了,这一次操作就会把它们变成黑的。贡献就是这个列数。

那么问题转化为求时刻 \(j \sim i\) 中,有多少列被涂白了,注意要去重。操作 \(2\) 的贡献也是同理的。\(j\) 是很好维护的,维护第 \(p\) 行,第 \(q\) 列上一次涂的时间 \(r_{p},c_{q}\) 即可。那么可以把询问序列离线下来然后扫描线用树状数组算。

不过这样太麻烦了。考虑一个更聪明的做法。我们已经维护了 \(r_p,c_q\),那么时刻 $j\sim i $ 中,有多少列被涂白了实际上就是求有多少个 \(c_q > r_p\),那么树状数组容易维护。时间复杂度 \(O(q\log n)\)

Record

P3531

如果没有重复的东西显然就是逆序对。然后考虑重复的字母,容易发现我们从左到右赋值做逆序对是对的。这是因为假设 S = AABA T = ABAA,那么 \(S\) 的第一个 \(A\) 一定是与 \(T\) 的第一个 \(A\) 配对的,以此类推。那么赋值完直接做逆序对就是对的。

NOIP 计划 R17 B

大意:给定一个长度为 \(n\) 的序列 \(a\),你最少需要多少次“交换相邻两项”的操作,才能满足这个序列先(非严格)增后(非严格)减。
很 cf 的题目。考虑 \(n \le 10\) 的子任务,枚举全排列,那么问题就变成怎么从枚举的序列变成序列。这个不就是 P3531。做完了。
考虑排列的子任务。这个神秘的限制有个很好的转化,就是从大到小插数,每次往左或往右插,这样就可以构造出一个合法序列。然后考虑代价怎么算,假设当前的数为 \(x\),如果往左边插的话,原序列 \(x\) 左边 \(> x\) 的数就要被交换,代价就是这个数的个数。往右边插是同理的。
感觉是上位绿。

NOIP 计划 R17 C

大意:对于一个元素介于 \([0,2^m)\) 且互不相同、长度为 \(n\) 的序列 \(a_1,a_2,\ldots,a_n\),定义它的特征序列\(p_0,p_1,\ldots,p_{2^m-1}\),其中 \(p_i\) 表示使得 \(a_{p_i}\)\(i\) 的异或值最大的下标。给定一个特征序列 \(p\),求有多少个满足要求的原序列 \(a\) 可以得到这个特征序列。答案对 \(10^9+7\) 取模。
咕咕咕。

P17089

不会。神了。

考虑 \(w_i \le 500\) 咋做。首先显然按 \(w\) 排序,\(f_{i,j}\) 表示前 \(i\) 个球,体力剩 \(j\)。你发现这样不太好转移,引入 \(k\) 表示前面选了的球到的最远的位置为 \(k\)
在对 \(i\) 进行转移的时候,考虑 \(k\) 的取值:

  • \(k \ge w_i\)\(f_{i,j,k+1} = \max(f_{i-1,j-x_i,k}+k+1, f_{i-1,j,k+1})\)
  • \(k < w_i\)\(f_{i,j,w_i} = \max(f_{i-1,j-x_i,k}+w_i,f_{i-1,j,w_i})\)

那么时间复杂度 \(O(n^3)\),可以滚掉一维。
然后考虑正解。这里有很多做法,比较容易得就是注意到有用的 \(w\) 的数量是 \(O(n)\) 量级的,离散化即可。这个可以这样证明:
考虑全部球都扔出去的情况,记此时球 \(i\) 所在位置为 \(t_i\),显然 \(t_i = \max(t_{i-1} + 1,w_i)\)。那么此时 \([w_i,t_i]\) 这段区间一定都是有球在的,此时没有球在的位置一定也不会有球了。于是记录离散化 \(t_i\) 即可。这样就把所有可能出现球的位置离散化完了。那么做完了。

mem(f, -0x3f); 
f[0][0][0] = 0; 

for (ll i = 1; i <= n; ++ i ) {
	for (ll j = 0; j <= s; ++ j ) 
		for (ll k = 0; k <= L; ++ k ) 
			f[i & 1][j][k] = f[!(i & 1)][j][k]; 
	ll h = Mp[B[i].w]; 
	for (ll k = 0; k <= L; ++ k ) {	
		ll g = Mp[to[k] + 1]; 
		for (ll j = B[i].X; j <= s; ++ j ) {
			if (to[k] >= B[i].w)
				f[i & 1][j][g] = max(f[i & 1][j][g], 
					f[!(i & 1)][j - B[i].X][k] + to[k] + 1); 
			else
				f[i & 1][j][h] = max(f[i & 1][j][h], 
					f[!(i & 1)][j - B[i].X][k] + B[i].w); 
		}
	}
}

P16131

In Luogu

首先考虑把复合串拆开,由于没有哪个初始字符串是另一个初始字符串的前缀,这一步容易用哈希实现。
那么得到一个长度为 \(k\) 的序列 \(p\),元素之间互不相同且由 \(1\sim n\) 组成。问题转化成求 \(p\) 的排名。
比较经典的 trick 是转化成求字典序比 \(p\) 小的序列数,这样可以按位来填。
考虑第 \(i\) 位,此时前 \(i-1\) 位都是被钦定的。记此时 \(n\) 个数里还没被钦定的,且 \(< p_i\) 的数有 \(f\) 个,那么如果在第 \(i\) 位填上 \(f\) 个数里的某一个,第 \(i+1 \sim k\) 位可以任意填,由于此时 \(n\) 个数里已经选了 \(i\) 个,方案数就是 \(f \times \binom{n-i}{k-i} \times (k-i)!\)。那么钦定第 \(i\) 位为 \(p_i\) 继续往下填即可。
\(f\) 的计算可以用树状数组或者 set 维护。时间复杂度 \(\mathcal{O(k\log n)}\)。注意由于我们求的是比 \(p\) 小的序列数,答案要 \(+1\),这里也要取模。赛时因为这个 \(100 \to 98\)

P15861

不会这个题还有救吗。

考虑 \(m = n(n-1)/2\),此时显然直接放 \(n\) 个相同元素是对的。考虑微调,记相同元素为 \(p\)。找到最大的 \(n_0\) 使 \(n_0(n_0 - 1)/2 < m\),记 \(t = m - n_0(n_0 - 1)/2\),要有 \(t\) 的贡献,只需要在第 \(t\)\(p\) 后插入一个 \(2p\) 就好了。那么做完了。注意特判 \(m=0\)

P4462

做一个异或前缀和。可以发现就是求满足 \(i,j\in [L-1,R]\)\(s_i \oplus s_j = k\)\((i,j)\) 对数,用莫队维护即可。时间复杂度 \(O(n\sqrt n)\)

P17161

进场了。
预处理出每个数的最小质因数。查表得知值域范围内 \(\omega(n) = 8\)。那么求质因数就是 \(O(\omega(n))\) 的。时间复杂度 \(O(T\omega(n))\),可能带个 \(\log\)

P2619

直接求一遍 mst,那么得到一个 \(k\) 条白边的 mst。分讨 \(k\)\(need\) 的大小:

  • \(k = need\)。请输入文本。
  • \(k < need\)。说明白边的边权都较大,考虑给这些边权同时减去一个值 \(w\),这样选择的白边数量只会变大。然后再求一遍 mst 得到 \(k'\),重复过程。
  • \(k > need\)。说明白边边权都较小,给边权同时加上一个值 \(w\),选择的白边数量会变小。再求 mst 得到 \(k'\)

于是直接二分 \(w\),注意相同边权要优先考虑白边。但是是可能存在一种情况找不到 \(k = need\),此时取 \(w+1\)\(k = need - 1\),取 \(w-1\)\(k = need +1\),这是因为存在某条黑边与新的白边边权相同,由于题目保证有解,直接用这些黑边替换掉就是对的。于是我们在 \(k \ge need\) 时记录答案 \(mst - need \times mid\) 即可。
代码:

ll l = -100, r = 100, Res; 
while (l <= r) {
	ll mid = l + r >> 1; 
	ll k2 = chk(mid); 
	if (k2 >= k) {
		Res = mst - k * mid; 
		l = mid + 1; 
	}
	else r = mid - 1; 
}
cout << Res << '\n'; 

P13984

分块做法。我们维护 \(f_{i,j}\) 表示块 \(i\sim j\) 的最小众数,然后维护 \(g_{i,j}\) 表示数 \(i\) 在块 \(j\) 出现的个数。然后查询的时候,你先把整块的最小众数拿出来,然后考虑散块可能出现最小众数,暴力枚举即可。由于散块的贡献只有 \(\sqrt n\) 个,时间复杂度还是 \(O(n\sqrt n)\)

posted @ 2026-06-19 13:29  RainyRadio  阅读(19)  评论(0)    收藏  举报