杂题记录1
P13665 「TPOI-5D」「僕は…」
/bangbangt
缝合题,出题人压根没细想弱化版题目 CF547E。
考虑沿用 CF547E 的 AC 自动机做法,先差分然后离线,转化成 \(S=\sum|s_i|\) 次单修, \(q\) 次区查,然后用树状数组。
把这个题套过来是简单的,对 \(L,R\) 分块,散块考虑用上边的做法,将原本的树状数组改成 \(O(\sqrt n)\) 修改 \(O(1)\) 查的分块即可。
对于整块部分,我们对每个整块的贡献一起算,我们 \(O(n)\) 求出串对多少个查询区间有贡献,记作 \(c_i\) ,那么对于一个询问答案就是 \(c_l\) ~ \(c_r\) 的和,对 \(c\) 做前缀和即可。
P8350 [SDOI/SXOI2022] 进制转换
被这题薄纱了。
首先取最小的 \(B\) 有 \(3^B > n\) ,然后从第 \(B\) 位往 \(0\) 做数位 dp
对于每个 \(i\in[1,B]\) ,我们取最小的数 \(mx_i\) 有 \(2^{mx_i} > 3^i\)
定义 \(dp_{i,s,{0/1},{0,1}}\) 为 dp 到第 \(i\) 位,二进制下当前数后 \(mx_i\) 位为 \(s\) ,数位 dp 是否顶满,是否要从 \(i-1\) 进位过来。
最后一维非常关键,因为假如我们只存 \(s\) ,那么无论我们存多少位,都会有进位的情况,然后依然会有后效性。
转移的时候考虑刷表,从 $dp_{i+1,s,j} $ 刷表转移到 \(dp_i\) ,我们枚举三进制下 \(i\) 这一位选的数为 \(x\) ,如果 \(s+x\times3^i\) 会发生进位,我们就从 \(dp_{i+1,s,j,1}\) 转移过来,接着考虑转移系数,对于 \(y\) ,系数应该是 \(s_x\times 3^i\) 在二进制下 \(mx_i+1\) ~ \(mx_{i+1}\) 之间 \(1\) 的个数。
对于 \(z\) 系数自然是 \(z^i\) ,对于 \(x\) 系数是 \(x^{3^i}\) 。
但是在 \(i\) 接近 \(B\) 时转移效率很低,因为 \(s\) 需要存 \(2^{mx_i}\) 种数。
考虑根号分治,对于 \(i\ge \frac B 2\) 的部分,我们暴力 \(3^{\frac B 2}\) 枚举每一位,然后更新 \(dp_{\frac B 2}\) 的值。
接着对于 \(i<\frac B 2\) 时我们再用上边的转移,此时 \(2^{mx_i} < 3^{\frac B 2}\) ,复杂度就是 \(B\times 3^{\frac B 2}\) 的。
最终复杂度近似于 \(\log_3 n \times \sqrt n\)
P11024 [COTS 2020] 定序 Redoslijed
比较 trick,但挺有趣的题。
显然我们应从后染色的往前考虑,我们称一个位置 \(i\) 被标记代表之前选过 \((l,r,c)\) 给 \(i\) 染过色。
那么我们的做法显然是一直选可以选的区间,然后看是否能选完且染色合法,而一个区间 \((l,r,c)\) 被选当且仅当区间 \((l,r)\) 所有位置被标记或者未被标记的点颜色均为 \(c\) 。
这个直接维护比较困难,考虑线段树的 trick,我们把 \((l,r,c)\) 挂到线段树上分到 \(\log\) 个区间上,那么只要所在的每个区间都合法我们就选上 \((l,r,c)\) 。
那么我们现在问题就变成了 \(n\) 次单点删除,以及维护一个线段树节点下是否所有点颜色全部相同或者全被删除完。
实现可以维护区间 \(max,min\) 判断 \(min,max\) 是否相等,还要注意关于 \(b_i = 0\) 时的细节问题。
P11845 [USACO25FEB] Min Max Subarrays P
在模拟赛之后又一次看到这个题,写篇题解。
手玩这东西 1h 多发现对于足够长的区间,假如区间长为奇数答案就是最大值,否则是次大值。
具体证明我也不是很会,但是我们可以转化一下,考虑答案为 \(ans\) 是否合法,我们把 \(\ge ans\) 的地方记为 \(1\) 否则为 \(0\) ,相当于要轮流做将相邻两个数变成它们的 或 或者 与,在区间长足够大的时候,我们在奇数区间长度基本能构造出最后只剩两个数 \(0,1\) 然后删去次大也就是 \(0\) 然后取到最大值,偶数区间最后剩 \(1,1\) 然后删最大取到次大,这两个都是最优解。
于是上边变成求长为奇数区间的最大值的和,以及偶数区间次大的和,分治一下这个是好求的(虽然很史)。
考虑区间长度 \(len \leq 10\) 的时候,我们考虑判断 \(ans\) 是否能作为答案,参照上边的做法转化成 \(01\) 序列,然后发现这东西一共就 \(2^{len}\) 种序列,于是记搜一下就行了。
很屎,考场写了上 200 行,但是一遍竟然写对了?
P2025 Dirichlet 半在线卷积
已知函数 \(f\) 满足 \(f(1)=1\),且
给定正整数 \(n\),试求出 \(f(1),f(2),\cdots,f(n)\) 的值。为控制输出量,你只需输出下式的值:
其中 \(\oplus\) 代表异或。
有趣的题。
有个巧妙的做法,首先考虑暴力的做法,考虑分治转移,左边求完过后转移给右边,暴力是 \(n\ln n\) 的。
考虑用 Dirichlet 前缀和优化,\(f*\varphi = f*id*\mu\)
其中 \(*id\) 可以在前缀和时带上 \(p_i\) 的系数,对于 \(\mu\) 其实就是差分,然后就做到 \(n\ln\ln n\) 了。
但是因为有大量的非连续数组寻址,所以建议不写递归写倍增,用 memcpy , memset 卡卡常。
给个实现,是评测机波动过的:
#include <bits/stdc++.h>
#define ll long long
#define rep(i, x, y) for (int i = (x); i <= (y); ++i)
#define drep(i, x, y) for (int i = (x); i >= (y); --i)
#define pb push_back
#define pii pair<int, int>
#define fi first
#define se second
#define mem(a, b) memset((a), b, sizeof(a))
#define ALL(a) (a).begin(), (a).end()
#define fastio ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int N = 50000010;
#define u32 unsigned int
bool vis[N];
int p[N / 10], cnt;
u32 g[N], f[N], n, ans;
int main() {
fastio; cin >> n;
vis[1] = 1;
rep(i, 2, n) {
if(!vis[i]) p[++cnt] = i;
for(int j = 1, x; j <= cnt; ++j) {
x = p[j] * i; if(x > n) break;
vis[x] = 1; if(i % p[j] == 0) break;
}
}
int l = __lg(n) - 1; f[1] = 1;
while(l >= 0) {
int m = (n >> l), lst = m >> 1;
memcpy(g, f, (lst + 1 << 2)), memset(g + lst + 1, 0, (m - lst << 2));
rep(i, 1, cnt) {
if(p[i] > m) break;
for(int j = 1; j * p[i] <= m; ++j) g[j * p[i]] += g[j] * p[i];
}
rep(i, 1, cnt) {
if(p[i] > m) break;
for(int j = m / p[i]; j; --j) g[j * p[i]] -= g[j];
}
memcpy(f + lst + 1, g + lst + 1, (m - lst << 2)), --l;
}
rep(i, 1, n) ans ^= f[i];
cout << ans << '\n';
return 0;
}
AT_agc005_d [AGC005D] ~K Perm Counting
简单题
一眼容斥,考虑求出 \(f_i\) 为选定至少 \(i\) 个位置 \(j\) 有 \(|A_j-j|=k\) 的方案数,然后答案就是 \(\sum (-1)^i f_i(n-i)!\) 。
然后考虑 \(f_i\) 怎么求,因为我们要让选定的点也合法,要保证不存在有两个位置有相同值,我们考虑把位置看作左部点,选的值看作右部点,对左边的 \(i\) 向右边 \(i-k(i>k)\) , \(i+k \leq n\) 连边,不难发现方案其实就是这个图选 \(i\) 条边,边不共点(边独立集)的方案数。
不难发现这个图中只有 \(i \bmod k\) 相同值的点直间有边,而且图是一条链。
我们考虑一条有 \(m\) 个点的链,选 \(i\) 条不相邻的边的方案数,这个等于 \(\binom {m - i} i\) 的,证明考虑隔板法,我们把 \(i\) 条不相邻的边视作隔板,那么就相当于 \(m\) 个点分到 \(i+1\) 个段,不过第 \(1\) 个盒子和第 \(i + 1\) 个段至少分一个,中间的段至少分两个(因为首尾都要连边但不能共点),然后这个考虑先全部减一然后隔板算,答案就是 \(\binom {m-i} i\) 的。
然后发现是个卷积的形式,是可以优化的。
数据范围比较小,直接暴力就能过。
P4528 [CTSC2008] 图腾
很石好的题目,考验我的基本功。
我们用四个数大小关系给四元组标成 \(4!=24\) 种情况, 不难发现答案是 \(1324\) 的方案数减去 \(1243,1432\) 的方案数。
我们把大小关系未定的位置记成 \(x\) ,首先 \(1243=12xx-1234\),这是简单的。
但还是不好做,考虑直接变换答案的式子
化到这一步考虑算 \(1x2x\) ,这个可以枚举 \(2\) 的值为 \(i\),然后将小于 \(i\) 的数的位置取为 \(0\),大于 \(i\) 的位置取为 \(1\),不难发现对于 \(x\) 的答案就是左边形如 \(01\) 的数对个数乘上右边 \(1\) 的个数,\(01\) 数对个数可以用线段树维护。
然后发现 \(14xx\) 还是不会算,我们考虑 \(14xx=1xxx-12xx-13xx\) ,带回去得到
然后考虑 \(13xx\) 怎么算,我们发现将 \(x,y\) 两维置换一下过后 \(13xx\) 就变成 \(1x2x\) 的情况了,然后就用之前的做法就行了。

浙公网安备 33010602011771号