CF510D Fox And Jumping
给定 n n n 张卡,每张卡有 a i a_i ai 和费用 c i c_i ci,选一些卡让这些卡的 a i a_i ai gcd 为 1 1 1,求最小的费用。 n ≤ 300 , a i ≤ 1 0 9 n \leq 300,a_i \leq 10^9 n≤300,ai≤109。
显然要 dp。设 f [ i ] [ j ] f[i][j] f[i][j] 表示考虑到第 i i i 张卡且 gcd 为 j j j 的最小费用。
f [ i ] [ j ] = min k = 1 i − 1 { f [ i ] [ ( j , a i ) ] + c i } f[i][j]=\min_{k=1}^{i-1}\{f[i][(j,a_i)]+c_i\} f[i][j]=k=1mini−1{f[i][(j,ai)]+ci}
时间复杂度 O ( n 2 V ) O(n^2V) O(n2V)。思考优化。
任何数的 gcd 都是这个数的因子。因此可以预处理出所有 a i a_i ai 的因子,然后做个映射。这样可以把空间降下来。还要滚动数组。
CF1294F Three Paths on a Tree
给定一棵 n n n 个结点的树,求三个点,使得这三个点路径的并集最大。 n ≤ 2 × 1 0 5 n \leq 2\times 10^5 n≤2×105。
显然肯定要选直径的两个点的,问题在于第三个点怎么选。很简单:求出直径之后,把所有直径上的点扔到队列里 bfs,离直径最远的那个点就是第三个点。
注意特判整棵树是个链的情况。以及直径的长度不等于经过的点数,像我一样用 dfs 的需要注意处理。
CF852B Neural Network country
这个比较说不清,建议看原题(洛谷那个翻译很差)。
给定一个有向分层图,每层都有 n n n 个点,分 L L L 层;每条边有权值 a i a_i ai。层与层之间的边连接方式、权值排列方式均相同。求有多少条从 S S S 到 T T T 的路径权值和为 m m m 的倍数。 L ≤ 1 0 5 , n ≤ 1 0 6 , m ≤ 200 L \leq 10^5,n\leq 10^6,m \leq 200 L≤105,n≤106,m≤200。
令 w i w_i wi 表示两层间权值为 i i i 的数量。
考虑 dp。设 f [ i ] [ j ] f[i][j] f[i][j] 表示到了第 i i i 层时余数为 j j j 的方案数。那么有:
f [ i ] [ j ] = ∑ k = 0 m f [ i ] [ j − w k ] f[i][j]=\sum_{k=0}^{m}f[i][j-w_k] f[i][j]=k=0∑mf[i][j−wk]
直接转移是 O ( n 2 m L ) O(n^2mL) O(n2mL) 的。但是你会发现,两层之间的转移是一模一样的。
f [ i + j ] = a [ i ] × b [ j ] f[i+j]=a[i]\times b[j] f[i+j]=a[i]×b[j]
也就是说这东西可以写成一个类似于卷的形式。能不能卷我不知道,不过反正这个是可以实现快速幂一样的转移了。
时间复杂度 O ( m log L ) O(m \log L) O(mlogL)。
dp 转移不一定要矩阵才能快速幂,如果两个阶段之间的转移一模一样,那可以考虑自己定义一种支持结合律的运算来快速幂。
P5355 [Ynoi2017] 由乃的玉米田
给定序列
a
a
a,每次询问给定一个符号 +-*/
之一、一个区间
[
l
,
r
]
[l,r]
[l,r]和一个数
x
x
x,问
x
x
x 能不能用那个符号以及区间里的任意两个数运算得到。
n
≤
1
0
5
,
a
i
≤
1
0
5
n \leq 10^5,a_i \leq 10^5
n≤105,ai≤105。
发现值域很小,不过线段树大概率是会 MLE 的。这里用莫队。思考每个操作怎么做:
- 加:开两个
bitset
,b1
保存 x x x 是否在区间内,b2
保存 V − x V-x V−x 是否在区间内。询问时返回((b1<<(maxv-x)&b2)).count()
即可。 - 减:更简单,返回
((b1<<x)&b1).count()
即可。 - 乘:枚举
i
∈
[
1
,
V
]
i \in [1,\sqrt V]
i∈[1,V],如果
x
i
\frac{x}{i}
ix 在
b1
里则返回 true。 - 除:枚举
i
∈
[
1
,
V
]
i \in [1,V]
i∈[1,V],如果
i
x
ix
ix 在
b1
里则返回 true。
可以发现瓶颈在除法:单次操作是 O ( n ) O(n) O(n) 的。
考虑根号分治。对于除法操作, ≥ V \ge \sqrt V ≥V 的询问复杂度是没问题的,所以把那些 x ≤ V x \leq \sqrt V x≤V 的单独分出来处理。
单次操作是 O ( V ) O(\sqrt V) O(V) 的,总时间复杂度是 O ( n V ) O(n \sqrt V) O(nV)。
那些单独分出来的怎么处理?可以枚举 x ∈ [ 1 , V ] x \in [1,\sqrt V] x∈[1,V],然后扫描整个数列。维护一个 p r e [ i ] pre[i] pre[i] 表示数字 i i i 上一次出现的位置、以及一个指针 l l l,然后当 p r e [ a i v ] pre[a_iv] pre[aiv] 或 p r e [ a i v ] pre[\frac{a_i}{v}] pre[vai] 能够更新 l l l 的时候,更新。之后就可以 O ( 1 ) O(1) O(1) 回答询问了。
总时间复杂度是 O ( n V ) O(n \sqrt V) O(nV)