做题笔记

pwp

难度 - 题解 - 思考

P11702

难度:绿

显然的结论是最小值只由一个元素构成,枚举最小值,然后考虑最大值的段。由于元素非负,我们希望最大值的段尽量大,那么随便做。
思考:结论题。

P10369

难度:上位绿

显然答案 \(\le 2\)。难点在于怎么构造。打表。

0 1
1 2 0
0 0 1 1 
1 1 2 0 0 
0 0 0 1 1 1
1 1 1 2 0 0 0

那么过了。
思考:容易得到结论。构造的时候可以通过打表发现规律。

CF1305F

难度:青

大学习。
你发现令 \(\gcd = 2\) 是很棒的,这样答案就是序列里奇数的个数,不妨记为 \(t\),那么答案 \(\le t\)
然后考虑怎么样做到更优,你发现可能存在几个数,它们只要动一点点就满足条件了,一个很妙的结论是至少有一半的数,至多被操作一次,否则操作次数 \(\ge t\)
那么我们随机选一个数出来,有 \(\frac{1}{2}\) 的概率选到至多操作一次的数,记为 \(x\),我们把 \(x-1,x,x+1\) 分别质因数分解,把质因子拎出来当 \(\gcd\) 求答案,就有 \(\frac{1}{2}\) 的概率求到正确答案。多随机几个就是对的。
查表知道值域范围内质因子最多 \(12\) 个,跑个三四十次就是对的。
思考:随机化的思想。\(\gcd = 2\) 可以作为一个很好的答案,考虑要怎么做到更优,考虑更优的情况大多数的元素至多操作 \(1\) 次,然后就可以随机化。

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)\)。滚动一下数组就过了。
没滚动数组的代码
思考:简单 dp。这种排列问题一个 trick 是从小到大填数。

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)\)
思考:仍然是 dp,从小到大填数。然后要考虑 \(i\) 带来的贡献,讨论没有贡献 / 有贡献,进行计数即可。是很经典的题目。

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\) 的数就要被交换,代价就是这个数的个数。往右边插是同理的。
思考:想到从大到小插数这个转化就很容易了。

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); 
		}
	}
}

思考:朴素 dp 需要把前面球的最远位置放入状态方便转移,优化的时候要注意到有用的 \(w\) 数量很少。决策点很少。

P16131

难度:绿
场切了。

首先考虑把复合串拆开,由于没有哪个初始字符串是另一个初始字符串的前缀,这一步容易用哈希实现。
那么得到一个长度为 \(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\)
思考:按位来填的 trick。貌似手搓了个康托展开。

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\)
思考:如果只是要求某个数质因子的个数或集合,可以预处理每个数的最小质因子。而非 \(O(\sqrt V)\) 地取除。

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'; 

思考:一个经典 trick。没啥好说的,注意最后的处理。

P13984

难度:紫

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

P1772

难度:青
我咋不会这个题。

最优性问题考虑 dp。\(f_i\) 表示前 \(i\) 天的最小代价,转移的时候枚举 \(j\),有 \(f_i = \min(f_i,f_{j-1} + (i-j+1) \times L + K)\),其中 \(L\) 可以单独算。时间复杂度 \(O(n^2m^2)\)
思考:看到 \(n\) 这么小就应该想到 dp!!

P1084

难度:下位紫
严肃不会。。。

考虑二分 \(t\)。由于每个军队独立的,互不影响,对每个军队分别考虑。你发现我们总是希望军队向上走,贪心地覆盖更多点。于是对每个军队计算在 \(t\) 时间内能走到深度最浅的节点。如果走到根了就下移一步。容易用倍增维护。
你发现对于那些走不到根的军队,它们没有办法再产生更优的贡献。那么它们的走法就固定了。然后考虑根结点的某个子节点 \(u\),如果 \(u\) 为根的子树里不能被覆盖,就说明需要其它子树给一个点走到 \(u\)
我们来考虑 \(u\) 的军队情况,记 \(u\) 到根结点的距离为 \(dis_u\)

  • 如果 \(u\) 上没有军队而它又需要被覆盖的话,需要别的子树给它一个,这个稍后考虑。
  • 如果 \(u\) 上有军队而它不用被覆盖,它上面所有的军队都可以拿出去贡献(判掉军队只能走到 \(u\) 的情况)。
  • 如果 \(u\) 上有军队而它需要覆盖,贪心地把剩余时间最小的点放在 \(u\),剩下的拿出去贡献。但是要考虑一个特殊情况,就是这个剩余时间最小的点可以帮助覆盖另一个 \(u'\) 的子树,而另一个子树的 \(u''\) 可以帮助覆盖 \(u\) 的子树,显然可以构造出这种情况。怎么解决呢?你发现如果一个军队的剩余时间不足以从 \(u\) 走到根再走回 \(u\),也就是剩余时间 \(t< 2dis_u\),这个军队要出去贡献也是贡献 \(dis_{u'} < dis_u\)\(u'\),然而需要让另一个 \(u''\) 来覆盖 \(u\),显然是不优的。于是如果存在一个军队 \(t < 2dis_u\),就把它留在 \(u\),剩下的出去贡献。

ok 那现在考虑多余的点怎么贡献。对于在 \(u\) 上的空闲军队,维护 \(W = t - dis_u\),那么 \(dis_{u'} \le W\)\(u'\) 都可以被贡献。将 \(W\) 和需要贡献的 \(dis\) 拉出来排序,贪心匹配即可。
思考:我们让军队贪心往上走,然后对于还能走的军队进行覆盖,进行大力分讨。

P7915

难度:绿
我咋只会 \(O(n^2)\)。。。不过看题解区这个做法是可以卡过去的。

这个策略还是挺难想的,想出来就很容易了。直接参考第一篇题解。很难解释这个思路就不再写了。
思考:思维题,没招,多练吧。

P7961

难度:绿

考虑这个限制怎么满足,你发现对于某个二进制位,如果该位选了 \(p\) 次,就会给下一位贡献 \(\lfloor \frac{p}{2} \rfloor\)。刻画 dp,\(f_{i,j,l,p}\) 表示前 \(i\) 个二进制位(低到高),选了 \(j\) 个数,当前二进制位上的数是 \(l\)(加上了前面二进制位的贡献),前 \(i\) 位的 popcount 为 \(p\)。转移的时候枚举第 \(i\) 位选了 \(t\) 个数,从 \(f_{i-1,j-t,2(l-t),p-(l\bmod 2)}\)\(f_{i-1,j-t,2(l-t)+1,p-(l\bmod 2)}\) 转移。转移的贡献是 \(f \times a_i^t \times \binom{i}{t}\) 这个是容易推的。那么预处理幂次和组合数,在 \(O(n^2mk) = O(n^3m)\) 的时间解决了问题。常数很小。
思考:自己过的 dp,开心。设计状态比较难,设计出来就很好转移了。

P9870

难度:紫
这个转化太困难了也。

注意到 \(q\) 很小,所以只需要暴力修改每次线性判断复杂度就是对的。
不妨考虑 \(X_1 < Y_1\) 的情况,若 \(X_1 > Y_1\) 则取反就是对的。

暴力

考虑暴力怎么写。显然可以 dp,\(f_{i,j}\) 表示 \(X\) 匹配前 \(i\) 位,\(Y\) 匹配前 \(j\) 位是否可行。转移的时候若 \(X_i > Y_j\)\(f_{i,j}=0\),否则从 \(f_{i-1,j},f_{i,j-1},f_{i-1,j-1}\) 转移。容易做到单次询问 \(O(nm)\)

特殊性质

然后考虑这个东西本质是在干啥。巧妙的转化是记 \(A_{i,j}=X_i < Y_j\),你现在要从 \((1,1)\) 走到 \((n,m)\),每次可以往 \((i+1,j)\)\((i,j+1)\)\((i+1,j+1)\)\(1\) 走。也就是判断是否存在从 \((1,1)\) 走到 \((n,m)\) 的八连通路径。
然后考虑这个特殊性质,这相当于第 \(n\) 行和第 \(m\) 列都是 \(1\)。那么我们需要走到第 \(n-1\) 行或 \(m-1\) 列。考虑求出 \(1\sim n-1\) 的最小值 \(X'_{min}\),所在位置为 \(p\)。若 \(X'_{min} < Y_{min}\),这代表 \(A_{p,?} = 1\),可以走到第 \(m\) 列。那么问题又转化为需要走到第 \(p-1\) 行或第 \(m-1\) 列,递归下去处理。列的处理也是同理。不过列需要找的是最大值。
总结一下特殊性质的做法。一开始如果 \(X_{min} \ge Y_{min}\) 或者 \(X_{max} \ge Y_{max}\) 显然无解。记 \(f(x,y)\) 表示能否走到第 \(x\) 行或第 \(y\) 列,每次找到 \(1\sim x-1\)\(X_{min}\),位置为 \(p\),若 \(X_{min} < Y_{min}\),递归求解 \(f(p-1,y)\)。同理找到 \(1\sim y-1\)\(Y_{max}\),位置为 \(q\),若 \(X_{max} < Y_{max}\),递归求解 \(f(x,q-1)\)。注意由于边界的递减有单调性,递归求解之后不需要再回溯对另一个边界求,否则时间复杂度会退化。预处理前缀最值容易做到 \(O(nq+mq)\)

正解

会了特殊性质正解就很容易了。先找到全局 \(X\) 最小值与 \(Y\) 最大值,位置分别为 \(p,q\)。那么把 \((1,1)\)\((p,q)\) 这个矩形与 \((p,q)\)\((n,m)\) 这个矩形都拉出来做一遍特殊性质即可。前者做 \(p_{min},q_{min}\),后者做 \((p_{max},q_{max})\)。时间复杂度 \(O(nq + mq)\)

思考:特殊性质题,由特殊性质一步步考虑正解。其中转化成八连通很巧妙。

P2605

难度:紫

暴力

容易想到 \(f_{i,p}\) 表示 \(i\) 个村庄用了 \(p\) 个基站。容易暴力 dp,\(f_{i,p} = \min(f_{j,p-1} + W(j+1,i-1)) + C_i\)\(W(l,r)\) 表示 \(l\sim r\) 村庄的贡献,容易暴力维护。于是做到 \(O(n^2k)\)。滚动数组一下得到 \(f_i = \min(g_j + W(j+1,i-1))\)

正解

很难直接优化,我们从 \(W(l,r)\) 入手。对于 \(i\) 维护 \(L_i,R_i\) 表示左边 / 右边第一个能覆盖 \(i\) 的位置,那么对于 \(i\),如果 \([L_i,R_i]\) 不建村庄就会有 \(W_i\) 的代价。那么对于后面的某个 \(i' > R_i\),如果想从 \([1,L_i)\) 转移就会有 \(W_i\) 的贡献,那么给 \([1,L_i)\) 区间加 \(W_i\),转移球全局 \(\min\) 即可,容易用 sgt 维护。

答案怎么求呢?枚举最后一个建基站的位置 \(i\),答案就是 \(f_i + W(i+1,n)\)\(W(i+1,n)\) 怎么算呢?本质是计算 \(L_x > i\)\(W_x\) 之和。枚举后缀的时候动态维护即可。时间复杂度 \(O(n\log n)\)

实现上具体我们给 \(L_i,R_i\) 都扔个 \(i\) 给它。在处理完 \(L_i,R_i\) 后把 \(i\) 的贡献算上即可。

思考:本质上写出暴力 dp,然后优化转移方程。

P9871

难度:下位蓝

暴力

容易想到 \(O(nk)\) 的 dp。\(f_{i,0/1}\) 表示第 \(i\) 天选 / 不选。\(f_{i,0} = \max(f_{i-1,0},f_{i-1,1})\)\(f_{i,1} = \max(f_{j,0} - (i-j)d + W(j+1,i))\),其中 \(j \in [i-k,i-1]\)\(W(j+1,i)\) 表示满足 \(k \in [j+1,i]\)\(y_k \le k - j\)\(\sum v_k\)。这个还是很好维护的。那么做到 \(O(nk)\),获得 \(36\) 分。

进一步优化?

拆一下转移方程。\(f_{i,1} = -id + f_{j,0} + jd + W(j+1,i)\),关心 \(f_{j,0} + jd + W(j+1,i)\)。显然要从 \(W(j+1,i)\) 入手,由于 \(k-y_k \ge j\)\(v_k\) 会贡献,又因为很好的东西是 \(k \in [j+1,i]\),对于每一个 \(k\) 我们给 \(j\in[0,k-y_k]\) 贡献 \(v_k\) 即可。这个可以区间加,区间 \(\min\) 做到 \(O(n\log n)\)。可以获得 \(56\) 分。

正解

题目给了我们一个很好地转化是把挑战看成线段,具体地,对于一个 \((x_i,y_i,v_i)\) 的挑战,看成 \((l_i = x_i-y_i+1,r_i = x_i)\) 的一条线段,权值为 \(v_i\)。然后现在我们要用若干条长度不超过 \(k\) 的线段去覆盖,如果某条给定的线段被完整覆盖,就会有贡献。

然后贪心地想,你肯定我们用来覆盖的线段,肯定左端点、右端点都是某条给定的线段的左端点。那么 \(f_{i,1}\) 只在 \(i = r_?\) 有用,然后用来转移的 \(j\) 只在 \(j = l_? - 1\) 有用。也就是说,有用的决策点只有 \(l_i-1,r_i\)\(2m\) 个!!于是把这些决策点离散化,套用上面的做法就是对的。

然后有个问题是 \(f_{i,0}\) 需要 \(i-1\) 的值,那咋办。显然这个东西的本质是 \(f_{i,0} = \max(f_{j,1})\),只需记录前缀最大值即可。时间复杂度 \(O(m\log m)\)

思考:写出暴力 dp 获得 \(36\),进一步优化容易做到 \(56\)。然后观察到有用的决策点很少,那么做完了。

P8868

难度:紫

暴力

注意到数据随机。容易想到按最大值分治。不妨按 \(A\) 序列的最大值分治。对于 \([l,r]\),找到其最值所在位置 \(p\),分裂成 \([l,p-1]\)\([p+1,r]\)。那么 \(A\) 的最大值确定了,考虑 \(B\) 的最大值。我们先考虑 \(B\) 的最大值在中点左侧的情况,可以在左侧每次跳前缀最大值,然后在右侧找到使得这个最大值合法的最大右端点,贡献容易计算。时间复杂度期望 \(O(qn\log n)\)。可以获得 \(36\) 分。

进一步优化

考虑数据不随机。仍然分治。但是是序列分治。考虑跨越区间中点的贡献,先考虑 \(A,B\) 最大值都在同侧的情况,在某侧枚举区间的左 / 右端点 \(i\),容易计算出另一侧最远合法的 \(j\),贡献容易计算。用双指针维护。
考虑最大值不在同侧的情况,仍然枚举 \(i\),这个时候可以得到区间 \([j,k]\) 表示端点在区间内的都合法。于是仍然双指针维护区间。时间复杂度 \(O(qn\log n)\),可以获得 \(52\) 分。

思考:分治分治分治。正解不会。

P3572

难度:绿
要求我们 \(O(nq)\) 过。强制使用单调队列,那咋办。

需要思考的地方在弹出队尾的时候,记队尾为 \(b\),当前为 \(i\)。那么 \(f_b > f_i\) 显然直接弹掉,否则如果 \(f_b = f_i\),我们取 \(h\) 更大的那个。那么做完了。出题人好心没卡 stl。
思考:写出朴素 dp。单调队列弹出的时候需要思考。

P4158

难度:绿

显然 \(f_{i,j}\) 表示前 \(i\) 行,涂了 \(j\) 次,每行当一个背包做。现在对每行要做一个 dp,\(g_{i,j}\) 表示考虑前 \(i\) 个字符,涂了 \(j\) 次,能涂到最多的格子数。
然后声称我们涂的区间肯定不会有交集,否则交集是无用的,肯定可以变成两个不交的涂色区间。
于是转移枚举当前涂色区间的右端点,\(g_{i,j} = \max(g_{k,j-1} + W(k+1,i))\)\(W(l,r)\) 表示 \(l\sim r\)\(0,1\) 数量最多的那个数量。前缀和容易维护。那么做完了。
思考:结论是,涂的区间肯定不会有交集。

P7414

难度:蓝

类似的,我们同样声称涂的区间肯定不会有交集。考虑区间 dp。\(f_{i,j}\) 表示把区间 \([i,j]\) 上色需要最小的操作数。板子是枚举 \(k\)\(f_{i,j} = \min(f_{i,k} + f_{k+1,j})\),这个显然是对的。然后在 \(A_i = A_j\) 的时候 \(f_{i,j} = f_{i+1,j-1}+1\),但是这个是不对的,因为可能 \([i+1,j-1]\) 涂色时可以刚好把 \(l,r\) 覆盖到,这样就多算的贡献。于是变成 \(\min(f_{i,j-1},f_{i+1,j})\),因为肯定可以把某次操作覆盖到 \(l/r\)。时间复杂度 \(O(n^3)\)
思考:区间 dp 的变形。

P3847

难度:绿

类似的区间 dp。\(f_{i,j}\) 表示区间 \([i,j]\) 的答案。转移的时候,若 \(A_i=A_j\),有 \(f_{i,j} = f_{i+1,j-1}\),否则 \(f_{i,j} = \min(f_{i+1,j},f_{i,j-1},f_{i+1,j-1})+1\),容易发现这三个状态都能再通过一次操作来变合法。
思考:仍然是区间 dp,把题目的操作转化成几个合法的状态。

P11233

难度:青
时隔两年还是不会。我喜欢你。这个 dp 也太牛了。

接下来是一万种 dp 做法。

Sol1

发现每个时刻(状态)我们总是在考虑要把当前的数染成红色还是蓝色,假设我们在染 \(i\)。那么 \(i-1\) 肯定是某个颜色的末尾,然后我们记录另一种颜色的末尾为 \(v\)\(f_v\) 表示另一种颜色末尾为 \(v\) 的最大价值。转移的时候分讨 \(i\) 要放哪个:

  • \(i-1\)。那么所有 \(f_v \to f_v + [A_i = A_{i-1}]A_i\)
  • \(v\)。注意此时另一种颜色的末尾变成 \(A_{i-1}\) 了,我们要对 \(f_{A_{i-1}}\) 进行更新。如果 \(v = A_i\),贡献是 \(f_v + v\)。否则贡献是 \(\max(f_{v})\),其中 \(v \ne A_i\)

前者是区间加,后者需要求 \(f_{A_i}+A_i\) 以及除了 \(f_{A_i}\) 之外 \(f_v\) 的最大值,由于 \(A_i > 0\),直接维护全局 \(f_v\) 的最大值,再与 \(f_{A_i} + A_i\) 取最大值即可。这个不用数据结构就能做。

Sol2

考虑更正常一点的 dp。\(f_{i,0/1}\) 表示第 \(i\) 个元素涂红 / 蓝色。很难转移,从贡献的角度出发。如果 \(i\) 没有贡献,显然 \(f_{i,0/1} = \max(f_{i-1,0/1})\)。否则 \(i\) 有贡献,考虑 \(f_{i,0}\),存在前面某个 \(j\) 满足 \(A_i=A_j\),给 \(j\) 涂红色,\([j+1,i-1]\) 涂蓝色。一个贪心是我们肯定找最近的 \(A_j=A_i\),否则贡献不优。那么考虑计算贡献:

  • \([1,j]\),贡献为 \(f_{j,0}\)
  • \((j+1,i)\),贡献容易预处理。
  • \(j+1\),你需要考虑 \(j\) 之前第一个涂蓝色的点,这个很难受。你发现我们钦定了 \(j\) 是最近的满足 \(A_i=A_j\)\(j\),于是 \(f_{j+1,1}\) 就是对的。。。因为 \(A_{j+1} \ne A_j\),在计算 \(f_{j+1,1}\) 的时候 \(j+1\) 不可能和 \(j\) 同色。
  • \(i\),显然是 \(A_i\)

总结一下就是 \(f_{i,0} = \max(f_{i-1,0/1},f_{j,0} + W(j+1,i) + f_{j+1,1})\)。其中 \(j\)\(A_i\) 上一次出现的位置。\(f_{i,1}\) 同理。那么做完了。时间复杂度 \(O(Tn)\)

思考:dpdpdp,第二种 dp 的关键是注意到转移肯定是从最近的相同颜色转移,同时 dp 可以从贡献的角度出发。第一种 dp 是另一种观察。

P3084

难度:紫

这个题有点牛。考虑 dp,\(f_i\) 表示前 \(i\) 头奶牛,第 \(i\) 头必需选的答案。\(f_i = \max(f_j)+1\),这是转移方程。然后我们考虑限制怎么满足,来得到合法 \(j\) 的决策点。
每个区间有且仅有 \(1\) 头奶牛被选。那么转化成最多 \(1\) 头和最少 \(1\) 头来考虑。

最多 \(1\)

对于某个区间 \([l,r]\),如果 \(i\) 被包含,那么 \(j\) 有个要求就是 \(j < l\)。因此需要找到包含 \(i\) 的所有区间中左端点最小的那个 \(l'\),有 \(j < l'\) 的要求。

最少 \(1\)

对于某个区间 \([l,r]\),如果 \(r < i\),那么 \(j\) 有个要求是 \(j \ge l\),否则 \([l,r]\) 就没东西了。因此找到所有 \(r < i\) 的区间中 \(l\) 最大的那个 \(l'\),有 \(j \ge l'\) 的要求。
于是现在要干啥。对于每个奶牛 \(i\),现在要找到两个东西:

  • 找到所有包含 \(i\) 的区间中左端点的最小值。记 \(h_i\) 表示这个东西,式子就是 \(h_i = \min(l_i, h_{i+1})\)\(l_i\) 表示右端点为 \(i\) 的区间的左端点。注意由于 \(h_{i+1}\) 可能 \(> i\),判掉这种情况即可。
  • 找到所有右端点 \(<i\) 的区间中左端点的最大值。记 \(g_i\) 表示这个东西。这个容易扫描线,区间按右端点排序后暴力加右端点 \(< i\) 的区间进来即可。

那么求出了 \(l_i\)\(g_i\)。转移随便用 ds 优化即可。由于 \(l_i,g_i\) 有单调性,可以用单调队列维护。偷懒写 sgt。
思考:刚好转化成至少和至多,然后这个分讨太厉害了。

P8511

难度:紫
lxl 有点强了。

不考虑在子树内这个限制。那么问题就变成任意选两个点使得异或权值最大,字典树模板。假设当前找到的点对为 \((x,y)\)
那么对于那些子树里不包含 \(x,y\) 的,答案就是 \(a_x \oplus a_y\)。对于那些子树含 \(x,y\) 的,那咋办。这些点显然是 \(1\sim x\) 路径上和 \(1\sim y\) 路径上点的并集。
然后对这两条链分别做一遍,假设我们当前在 \(x\),往下走到 \(y\),那么 \(x\) 以及所有 \(x\) 的子节点 \(v(v \ne y)\) 的子树都可以被算入贡献。容易用字典树维护。那么做完了。
思考:弱化限制,再考虑限制。好像挺难写。

P2831

难度:绿

容易想到状压。转移的时候枚举还没选的点 \(i\) 以及另一个没选的 \(j\),计算出 \(i,j\) 对应的函数解析式,再求出有多少点在这个函数上,进行转移。容易做到 \(O(2^nn^3T)\)
优化一下,显然可以预处理优化掉一个 \(n\),做到 \(O(2^nn^2T)\)。直接过了。
当然还可以优化。由于我们是刷表进行转移,对于每个状态,我们只需找到最小的还没选的点,用这个点来更新。因为即使这次转移不选这个点,你后继的状态还是要选这个点,先做和后做本质不用区别。于是每次做最小的点即可。时间复杂度 \(O(2^nnT)\)
思考:dp 转移状态的时候,没必要一次性把状态考虑掉,如果某个前驱可以被另一个前驱考虑掉,那么就可以不考虑。有效减少状态数。

P2254

难度:下位青
有点神秘。

容易想到朴素 dp。\(f_{t,i,j}\) 表示第 \(t\) 个时刻在点 \((i,j)\),转移是容易的,理论上可以获得 \(50\) 分。
你考虑它为啥要给你区间。容易想到在区间上 dp,\(f_{t,i,j}\) 表示当前在第 \(t\) 个区间的末尾,已经走到了 \((i,j)\),等价于往某个方向最多走 \(k=len_t\) 步。转移的时候 \(f_{t,i,j} = \max(f_{t-1,i',j'}) + ?\),容易用单调队列维护。时间复杂度 \(O(knm)\),可以用 sgt 带个 log。
思考:观察到给的是区间,启发在区间上 dp。

P14525

难度:下位绿
太强大了。

这个权值很诡异。记矩阵的长为 \(h\),宽为 \(w\)。不妨钦定 \(\min(h,w)=h\)。我们枚举 \([i,i+h-1]\) 钦定一下矩阵的长,那么把每一列看出一个数,做一遍长度 \(\ge h\) 的最大子段和就是对的。同理钦定 \(\min(h,w)=w\) 再做一遍即可。
思考:关心最小值就给它确定掉,然后每一行 / 列就可以看成一个数。

P8903

难度:上位绿
有点变态。

考虑朴素 dp,容易做到 \(O(n^4)\),结合前面的性质可以获得 \(50\) 分。
然后需要一个贪心策略。我们称牟尼为,冰激凌甜筒为甜筒。你发现在固定下来选的奶牛时,我们把甜筒给 \(X\) 更小的奶牛是更优的,这样剩下的钱会更多。于是把 \(X\) 小到大排序,然后对前缀进行 dp,\(f_{i,j}\) 表示前 \(i\) 头牛,用了 \(j\) 个甜筒能得到的最大欢迎度。也就是说 \(i\) 之前的奶牛我们都用甜筒付钱。再对后缀做 dp,\(f_{i,j}\) 表示第 \(i+1\sim n\) 头奶牛,用 \(j\) 块钱能得到的最大欢迎度。那么把前后缀合并就是对的。注意第 \(i\) 头牛可以用甜筒也可以用钱付。时间复杂度 \(O(n^2)\)
思考:贪心与 dp 的结合,先做到 \(O(n^4)\),然后贪心地想,肯定把甜筒给 \(X\) 更小的奶牛,排序完做前后缀 dp 即可。

CF597C

难度:下位绿

简单 dp,\(f_{i,j}\) 表示以 \(i\) 结尾,长度为 \(j\)。那么建 \(k\) 个 bit,每次从 \(f_{i,j}\)\(f_{x,j-1}\) 其中 \(x < i\) 转移,在第 \(j-1\) 个 bit 查询前缀和即可。
思考:naive

P4147

难度:绿

最大子矩阵问题。
考虑悬线法。\(f_{i,j}\) 表示往上最远能到哪,转移的时候若 \((i,j)\) 不是障碍,\(f_{i,j} = f_{i-1,j}+1\)。再维护 \(L_{i,j},R_{i,j}\) 表示这条悬线向左 / 右最远能到哪。转移 \(L_{i,j} = \max(L_{i,j},L_{i-1,j})\)\(R_{i,j}\) 同理。这样可以枚举出所有极大子矩阵,从而在 \(O(nm)\) 的时间复杂度求得答案。
思考:要求最大子矩阵,可以枚举所有极大子矩阵,来求得答案。

P3400

难度:青

求全为 \(1\) 的子矩阵数量。维护 \(up_{i,j}\) 表示点 \((i,j)\) 向上最远能到哪。然后维护 \((i,j)\) 表示子矩阵的右下角,计算合法的左上角的个数。假设当前在枚举第 \(i\) 行,然后在第 \(j\) 列,你发现会影响当前答案的 \(0\) 的位置总是单调递减的,因为如果一个 \(0\)\(j\) 左侧,但它位置比 \(j\) 对应的高,那就没用了。于是单调栈去维护,然后我们得到了上一个 \(j' < j\),满足 \(up_{i,j'} < up_{i,j}\),显然 \((i,j')\) 对应的答案是能继承的,然后额外的贡献就是 \((j-j') \times (i-up_{i,j})\),那么做完了。时间复杂度 \(O(nm\log n)\)
思考:这类矩阵计数问题,无非就是枚举右下角,然后考虑合法的左上角个数。本题里注意到有用的 \(0\) 点位置需要单调递增,计数即可。

P8783

难度:黄

求子矩阵和 \(\le V\) 的子矩阵个数。我们枚举矩阵的左边界 \([l,r]\),相当于用两条线切出了中间一块区域,然后对于每一列,\([l,r]\) 可以压缩成一个值,然后变成一维,双指针维护和 \(\le V\) 即可。
思考:naive

posted @ 2026-08-29 13:52  RainyRadio  阅读(15)  评论(0)    收藏  举报