Week 1

大学习。模拟赛题的话只记录赛时不会 / 猜过去的题。

数据结构

P2048

难度:紫
【模板】炒鸡钢琴。

实际上可以取 \(k\) 个不同的区间,每次要求区间长度在 \([L,R]\) 中。
我们对原序列做前缀和 \(S_i\),考虑区间的右端点 \(p\),,那么要求左端点 \(q\)\([p-R+1,p-L+1]\),我们希望区间和尽量大,也就是找到 \(S_p - S_q\) 最大,其中 \(q \in [p-R,p-L]\),不妨记这个区间的决策点是 \(x\),得到的答案为 \(v\)
那么就得到了一个合法的答案 \((l=p-R,r = p-L,x,p,v)\) 表示右端点是 \(p\),决策点是 \(x\),答案是 \(v\),可决策的区间是 \([l,r]\),我们把答案塞进大根堆里,然后考虑怎么由这个答案扩展出更好的答案,显然可以在 \([l,x-1]\)\([x+1,r]\) 求解决策点,选最优的 \(x'\),不妨假设在左边,那么得到新的答案 \((l,x-1,x',p,v')\),再塞入堆里。这样就在 \(O(k\log n)\) 解决了问题。求解决策点可以使用 sgt 维护。
思考:板子,妙妙 trick。

P1955

难度:下位绿

简单题。先把相等关系全部合并,再依次处理不等关系即可。
思考:naive

P4513

难度:青

最大子段和板子。可以维护每个区间的最大前缀和、最大后缀和、最大子段和,pushup 的时候容易合并。
思考:板子。

P6619

难度:蓝
【模板】树状数组上二分。

妙妙 trick。首先容易在 bit 外面二分做到 \(O(n\log^2 n)\),获得 \(60\) 分。
可以在 sgt 上二分去掉一只 log,但是常数太大(好像能卡过去),考虑 bit 上二分。
在 bit 上,第 \(i\) 个点储存的实际上是 \((i-lowbit(i),i]\) 的和。然后二分的时候,考虑一个倍增状物,每次考虑 \(2^{20},2^{19},2^{18},\cdots 2^1,2^0\) 能不能跳,怎么快速计算和呢?假设当前在 \(p\),考虑步长 \(2^k\),显然 \(2^k\) 肯定是 \(p+2^k\) 的 lowbit,我们现在计算了 \([1,p]\) 的和,需要计算 \((p,p+2^k]\) 的和,然后因为 bit 上节点 \(p+2^k\) 表示的是 \((p+2^k-2^k,p+2^k]\) 的和,所以这个点的值就是对和新的贡献。那么去掉一只 log。

CF438D

难度:蓝

与花神那题类似。取模不好取,考虑暴力就是对的。这是因为你的数 \(x\) 对某个模数 \(p(p < x)\) 取模,有 \(x\bmod p < \frac{x}{2}\)。于是一个数至多 \(\log\) 次,sgt 维护区间最大值即可。
思考:考虑暴力。考虑取模的性质。

随机跳题

P10537

难度:绿

伪交互。思考为什么不会无解,因为你删掉某个叶子后,又会出现新的叶子,于是 \(K=1\) 总是合法的。
由于我们希望 \(K\) 尽量大,显然能划分就划分。考虑一个区间能划分的充要条件:

  • 所有志愿者的叶子集合相同。容易哈希判断。
  • 掉叶子的顺序合法。可以记录每个叶子的儿子数量,可以做到 \(O(nm)\)。但是我写了类似二次方的东西不知道为啥过了。

思考:刻画出区间合法的充要条件,模拟就能过。

P4053

难度:青

反贪板子。楼房的优先级相同,不妨按截止时间小到大排序。然后每次能建就建,不能就判断扔掉当前一个 \(t_1\) 最大的会不会更优,能就扔掉,可以用大根堆维护。至于为什么要按截止时间排序,考虑邻项交换。
考虑当前用了 \(t\) 点时间。考虑 \(i,i+1\) 要不要交换。交换后更优的情况就是由只能选 \(1\) 个变成两个都能选,刻画一下柿子:

\[t + t_{1,i}+t_{1,i+1} > t_{2,i} \\ t + t_{1,i}+t_{1,i+1} < t_{2,i+1} \]

显而易见,按 \(t_2\) 小到大排序是对的。
思考:这种反悔贪心,重要的就是确定决策顺序,考虑邻项交换。

P11328

难度:青

模拟赛遇到了。考虑最优顺序怎么决定。考虑邻项交换,假定当前等级为 \(w\),考虑 \(i,i+1\) 交换后会不会更优。更优的情况显然是交换前只能选 \(i\),交换后 \(i,i+1\) 都能选。刻画一下柿子:

\[w+X_i > L_{i+1} \\ w + X_{i+1} \le L_i \]

整理得到 \(L_{i+1}-X_i < w \le L_i - X_{i+1}\),即 \(L_{i+1} + X_{i+1} < L_i + X_i\) 时,情况得到改善,于是按 \(L+X\) 升序排决策就是对的。
思考:同上。

P7410

难度:绿

模拟赛遇到了。赛时写了三次方过了,显然是不对的。考虑把 \(<100\) 的数赋为 \(-\inf\)\(=100\) 的赋成 \(1\)\(>100\) 的赋为 \(0\)。问题转化成求有多少个子矩阵满足和 \(> 0\)。这不就是 P8783,容易做到三次方。
也可以考虑容斥。记 \(\le 100\) 的答案为 \(f\)\(< 100\) 的答案为 \(g\),答案就是 \(f - g\)。两个都相当于求全 \(1\) 矩阵的方案数,这不也就是 P3400,容易做到 \(O(nm)\)
思考:赋值完后就是好做的。或者考虑容斥考虑容斥考虑容斥。

P13085

难度:青

板子。复习一下。从高到低搜,记录当前在填第 \(u\) 位,上一个数是 \(v\),前导零和最高位限制为 \(zer = 0/1\)\(lim = 0/1\)。记忆化搜索即可。

ll dfs(ll u, ll lst, bool lim, bool zer) {
	if (u < 0) return 1ll; 
	if (lst >= 0 && ~f[u][lst][lim][zer]) 
		return f[u][lst][lim][zer]; 
	ll up = lim ? num[u] : 9; 
	ll ans = 0; 
	for (ll i = 0; i <= up; ++ i ) {
		if (abs(i - lst) < 2) continue; 
		if (zer && !i)
			ans += dfs(u - 1, -2, 0, 1); 
		else
			ans += dfs(u - 1, i, lim && (i == up), 0); 
	}
	return f[u][lst][lim][zer] = ans; 
}

思考:板子。

P9745

难度:下位蓝
树形 dp 好题!!!!

先拼好分。

Sub1

\(n \le 12\) 暴力枚举断掉的边。

Sub2

来考虑链的分。\(f_i\) 表示前 \(i\) 个点的答案,记 \(s_i\) 表示异或前缀和,那么枚举上一个断掉了 \((j, i)\)\(f_i = f_j \times (s_i \oplus s_j)\),对 \(j \in [1,i-1]\) 求和即可。做到 \(O(n^2)\)
看到异或就要想到拆位。按位来考虑,如果 \(a_i\) 的第 \(x\) 位是 \(1\),就与前面该位为 \(0\) 的数有贡献,这个容易维护。那么做到 \(O(n\log V)\)

Sub3

考虑值域小的情况。\(f_{u,x}\) 表示点 \(u\) 子树内,\(u\) 所在连通块异或值为 \(x\) 的答案(不考虑 \(u\) 所在连通块)。\(g_u\) 表示 \(u\) 子树内的答案(考虑 \(u\) 所在连通块)。
转移的时候,对于 \(u\),枚举儿子 \(v\),考虑 \((u,v)\) 要不要断,转移:

\[f_{u,x} = f_{u,x}\times g_v + \sum_y f_{u,x\oplus y} \times f_{v,y} \\ g_u = \sum_x f_{u,x} \times x \]

那么做到 \(O(nV)\)。可以获得 \(64\) 分。

正解

受求链的答案时拆位的启发,考虑拆位。\(f_{u,i,0/1}\) 表示 \(u\) 所在连通块异或值在第 \(i\) 位是 \(0/1\) 的答案(不考虑 \(u\) 所在连通块)。\(g_u\) 不变。

\[f_{u,i,0} = f_{u,i,0} \times g_v + f_{u,i,0} \times f_{v,i,0} + f_{u,i,1} \times f_{v,i,1} \\ f_{u,i,1} = f_{u,i,1} \times g_v + f_{u,i,0} \times f_{v,i,1} + f_{u,i,1} \times f_{v,i,0} \\ g_u = \sum_{bit} f_{u,bit,1} \times 2^{bit} \]

注意 \(f_{u,i,0/1}\) 的计算是并行的,也就是要先计算再赋值。那么做到 \(O(n\log V)\)

思考:先考虑部分分,写出依赖值域的朴素 dp,然后由于是异或联想到拆位,就做完了。本题是一道很好的树形 dp 练习题。

P9313

难度:下位绿
模拟赛题。

显然每次只会拿 \(1/3\) 个,且 \(3\) 个是连续拿的,那就可以 dp。
思考:第一句话是显然的。第二句话考虑分讨。选的物品必定连续。

P9311

难度:上位绿
模拟赛题。

人类智慧。先随 \(100n\) 个数出来询问,然后得到 \(100\) 个返回数。对于其中任意两个数 \(x,y\),把 \(\mid x-y \mid\) 放进下一次询问,要去个重。但是这样数不太够,我们需要 \(n\) 个,那就可以多构造几个,构造方法很多样。更好写的是,你每次取 \(3\) 个数出来,这样就肯定够了。

思考:随机返回一个数,那你也随机询问!

P12087

难度:紫
模拟赛题。

考虑朴素数位 dp。可以获得 \(28\) 分。
考虑带修咋办,你发现填数的时候,在数字没有限制的情况下,方案数相同。那么每次不用清空 dp 数组。数据太水可以通过。

ll dfs(ll u, ll lst, bool lim, ll D) {
	if (u < 0) return 1ll; 
	if (!lim && ~f[u][lst]) 
		return f[u][lst]; 
	ll up = lim ? num[u + D] : 9; 
	ll ans = 0; 
	for (ll i = 0; i <= up; ++ i ) {
		if (lst == 1 && i == 3) continue; 
		ans = (ans + dfs(u - 1, i, lim && (i == up), D)) % mod; 
	}
	if (!lim) f[u][lst] = ans; 
	return ans; 
}

while (q -- ) {
	ll op, l, r; cin >> op >> l >> r; 
	if (op == 1) {
		-- l; -- r; 
		cout << dfs(r - l, 0, 1, len - r) << '\n'; 
	}
	else {
		-- l; 
		num[len - l] = r; 
	}
}

P14665

难度:绿

典中典。考虑差分序列,那么极差就相当于在差分数组上求最小子段和 / 最大子段和。然后每次可以单点 \(+1\),另个点 \(-1\)
来考虑怎么计算答案,考虑二分答案 \(x\)。那么每次在当前的最小 / 最大子段和超出 \(x\) 时,就需要操作。那么得到需要单点加 / 单点减的操作次数 \(f/g\),答案即为 \(\max(f,g)\)。注意由于对全局 \(+/-1\) 对答案没有影响,多出来的操作可以扔掉。时间复杂度 \(O(n\log n)\)
思考:区间加减显然在差分数组上考虑。然后考虑二分。

P6442

难度:青
妙中妙熔池。

\(m \le20\) 显然状压,把每个箱子的玩具压成一个状态,问题转化成选择若干个箱子,使它们状态的或为全集 \(U\)
考虑 dp,\(f_{i,S}\) 表示前 \(i\) 个箱子,状态为 \(S\)。容易做到 \(O(n2^m)\) 获得 \(50\) 分。
这样没啥前途,有一个妙妙熔池。记 \(f_{S}\) 表示箱子状态是 \(S\) 子集的箱子个数,即 \(f_S \le n\)。转移 \(f_s = \displaystyle \sum_{x \in s} f_x\),容易用 sosdp 转移。然后记 \(g_S\) 表示固定 \(S\) 内的元素不选,其余随意选。那么记 \(S\) 的补集为 \(T\),有 \(g_S = 2^{f_T}\)。答案的话显然熔池,一开始答案是 \(g_0\),然后枚举哪些元素没减,给它减掉,其实就是熔池。答案就是 \(\displaystyle \sum_S (-1)^{|S|} g_S\)

P9312

难度:黑
模拟赛题。

注意到每个状态,我们买的灯笼照亮范围总是个区间,可以 dp。\(f_{u,x,y}\) 表示当前在山峰 \(u\),买的灯笼照亮了 \([x,y]\),记搜可以做到 \(O(nk^2)\) ??获得 \(60\) 分。

P5956

难度:红
竞选年度最不是题的题。

注意到 \(B^? \bmod (B-1)=1\),实际上我们要选出尽量多的数使它们的和能被 \(B-1\) 整除。注意到 \(a_i > 0\),你把所有数都选上再扣掉一个肯定就是对的。。

P1813

难度:青

注意到 \(n\le 100\)\(e \le 10000\),考虑把时刻放进状态。\(f_{i,j}\) 表示时刻 \(i\)\(j\),到达终点的最小时刻。枚举在起点等了多久,转移的时候实际上不用考虑待在原点不动的情况,因为这个东西是为你往下走服务的,可以在走的时候把到达时刻变为 \(\max(i, E_{st})\)
思考:观察数据范围,然后暴力 dp。还要思考待在原地不动的本质是啥。

P5435

难度:紫
【模板】二进制 gcd。

考虑 binary gcd,假设当前在求 \(\gcd(x,y)\),大力分讨:

  • \(x = y\),请输入文本。
  • \(\min(x,y)=0\),请输入文本。
  • \(2 \mid x\)\(2 \mid y\),只考虑 \(2\mid x\),有 \(\gcd(x,y) = \gcd(\frac{x}{2},y)\)
  • \(2 \mid x\)\(2\mid y\),同上,两边都除掉即可。
  • \(2\nmid x\)\(2\nmid y\)\(\gcd(x,y) = \gcd(x-y,y)\)

借助位运算快速实现除 \(2\) 的过程,理论上是 \(O(\log)\) 的,实际上跑的很快。

ll bgcd(ll a, ll b) {
	ll x = __builtin_ctz(a); 
	ll y = __builtin_ctz(b); 
	ll z = (x > y) ? y : x; 
	b >>= y; 
	while (a) {
		a >>= x; 
		ll del = b - a; 
		b = (a > b) ? b : a; 
		a = (del < 0) ? -del : del; 
		x = __builtin_ctz(a); 
	}
	return b << z; 
}

思考:科技。

P9401

难度:紫

观察到这个神秘值域,这启发我们啥。首先判掉全选 \(b\) 的情况,至少选 \(1\)\(a\) 那么答案 \(\le 5\times 10^5\),可以枚举答案。
假设当前在枚举答案 \(x\) 思考啥时候这个答案合法。对于那些 \(x\nmid a_i\)\(i\),我们需要 \(x\mid b_i\),于是以 \(a\) 为下标 \(b\) 为值,每次枚举 \(x,2x,3x\),你需要 \([1,x-1],[x+1,2x-1],\cdots\) 这些区间的 \(\gcd\) 能被 \(x\) 整除,使用 st 表维护 \(\gcd\) 即可。调和级数可知时间复杂度 \(O(n\ln n\log V)\)。注意相同的 \(a\),把所有的 \(b\) 合并成它们的 \(\gcd\) 即可。因为对于相同的 \(a\),要么全部取 \(a\),要么全部取 \(b\)。否则对答案的合法不会有啥影响。合并完之后时间复杂度就变成了 \(O(a\log a\log V)\),使用二进制 gcd 可以轻松通过。
思考:观察值域的性质。想到枚举答案,考虑答案是否合法就想到要枚举倍数和维护区间 gcd。再考虑优化。

P9364

难度:下位绿

随便猜了个结论还真过了。串的个数少,由于答案肯定是输入的某个串,现在需要判断这个串是否合法。大胆随机 \(300\) 个子串判断是否存在,可以哈希 + map,随便过。
正解的话我们考虑递推。按串的长度小到大排序。然后判断 \(s:[1,n]\) 是否合法,只需判断 \([1,n-1],[2,n]\) 是否合法。可以字典树。
思考:naive

P6240

难度:上位青
【模板】猫树分治。

好题啊。之前模拟赛碰到过这个 trick,现在才知道是猫树分治 \ll

前置知识

假设当前有两个容量为 \(V\) 的背包,你想要进行合并,朴素合并是 \(O(V^2)\) 的。但实际上可以维护前 / 后缀最值做到 \(O(V)\)

猫树分治

这个算法能解决的问题大概符合以下条件:

  • 静态。多组询问
  • 难以直接求解询问。但是如果知道区间中点 \(mid\) 到两个端点的答案,可以快速合并来得到整个区间的答案。

在本题里,假设当前在处理区间 \([L,R]\),处理被 \([L,R]\) 包含的询问。记 \(m\) 表示中点。
对于右端点 \(\le m\) 和左端点 \(>m\) 的区间,递归下去求解。否则我们求出 \(f_{p,V}\) 表示只考虑 \([p,m]\) 物品,容量为 \(V\) 的背包以及 \(g_{p,V}\) 表示只考虑 \([m+1,p]\) 物品,容量为 \(V\) 的背包,然后 \(O(V)\) 进行合并即可。时间复杂度 \(O(qt+tn\log n)\)
思考:板子。很秒的 trick

CF2259D

难度:下位绿

黄吧可能。一个构造是令一个集合的 \(mex = 0\),不妨为 \(C\),只需要满足 \(mex(A)+mex(B)\ge 2\max(mex(A),mex(B))\),令 \(mex(A)=mex(B)=1\) 即可。所以我们需要至少两个 \(0\),其它非零元素全部塞进 \(C\)。只有一个 \(0\) 显然无解。
思考:构造构造构造。得对上脑电波吧。

CF1627D

难度:黄

应该后续不会再开这类思维题了。这个题注意到值域小,考虑枚举答案。考虑一个答案是否合法的时候,枚举倍数即可。
思考:简单题。看到值域小就想到要枚举答案。

CF868F

难度:上位蓝

很牛的题目。首先考虑暴力的 dp,\(f_{i,j}\) 表示前 \(i\) 个数分成了 \(j\) 段。转移容易做到 \(O(n^2k)\),具体你需要去动态维护一个区间的贡献,可以双指针。
然后无非就两条路,一条是拆贡献用 ds 优化,一条就是考虑决策点。前者没啥前途,@rimu_awa 大手子猜了个单调的结论,打表发现是对的。在同一层上 dp,然后你对于 \(i\) 右移的时候,有个观察是上一层的决策点 \(j\) 总是单调递增的。
但是你直接维护上一层的决策点指针是不对的,可能当前下一个答案与现在的答案相同。这个时候你就没办法决定要不要移动。
考虑分治,假设求出了 \([l,r]\)\(mid\) 的 dp 值,决策点在 \(p\),那么 \([l,mid-1]\) 的决策点区间就变成 \([1,p]\)\([mid+1,r]\) 的决策点区间就变成 \([p,n]\)。这应该算一个通用的写法。
还有个问题,我们怎么快速计算区间贡献?借鉴莫队的想法,暴力就是对的。时间复杂度大概是 \(O(nk\log n)\)

ll W(ll l, ll r) {
	while (L > l) val += ct[A[-- L]], ++ ct[A[L]]; 
	while (L < l) -- ct[A[L]], val -= ct[A[L ++]]; 
	while (R > r) -- ct[A[R]], val -= ct[A[R --]];  
	while (R < r) val += ct[A[++ R]], ++ ct[A[R]];  
	return val; 
} 
void dfs(ll l, ll r, ll pl, ll pr, ll dep) {
	// 表示在计算 [l, r] 的 dp 值
	// 决策点只会在 [pl, pr] 中
	ll mid = l + r >> 1; 
	ll res = 1e18, pos = -1; 
	for (ll i = pl; i <= min(mid, pr); ++ i ) {
		if (f[i - 1][dep - 1] + W(i, mid) < res) {
			res = f[i - 1][dep - 1] + W(i, mid); 
			pos = i; 
		}
	}
	f[mid][dep] = res; 
	if (mid != l) dfs(l, mid - 1, pl, pos, dep); 
	if (mid != r) dfs(mid + 1, r, pos, pr, dep); 
}

U619821

难度:上位紫 / 下位黑

模拟赛题。没有人类了。
如何做到在做过 P6189 的情况下在本题获得 \(0\) 分?!!

posted @ 2026-09-01 19:57  RainyRadio  阅读(9)  评论(0)    收藏  举报