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_2\) 小到大排序是对的。
思考:这种反悔贪心,重要的就是确定决策顺序,考虑邻项交换。
P11328
难度:青
模拟赛遇到了。考虑最优顺序怎么决定。考虑邻项交换,假定当前等级为 \(w\),考虑 \(i,i+1\) 交换后会不会更优。更优的情况显然是交换前只能选 \(i\),交换后 \(i,i+1\) 都能选。刻画一下柿子:
整理得到 \(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)\) 要不要断,转移:
那么做到 \(O(nV)\)。可以获得 \(64\) 分。
正解
受求链的答案时拆位的启发,考虑拆位。\(f_{u,i,0/1}\) 表示 \(u\) 所在连通块异或值在第 \(i\) 位是 \(0/1\) 的答案(不考虑 \(u\) 所在连通块)。\(g_u\) 不变。
注意 \(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\) 分?!!
浙公网安备 33010602011771号