T1
100%
用循环语句输入 A i A_i A i 的时候判断一下是否等于 x x x ,如果 A i = x A_i=x A i = x 答案就增加 1 1 1 。
注意数据范围,x x x 和 A i A_i A i 应使用 long long \text{long long} long long 类型的变量来读入和处理。
时间复杂度 O ( n ) O(n) O ( n ) 。
T2
100%
两重循环,外层循环枚举数组第 i i i 项,内层循环计算 A i A_i A i 的 k k k 次幂。
注意取模,计算其中一项 k k k 次幂的过程中也可能爆 int \text{int} int 。
时间复杂度 O ( n k ) O(nk) O ( nk ) 。
T3
50%
记 V = min ( a , b ) V=\min(a,b) V = min ( a , b ) 。
直接从 1 1 1 到 V V V 枚举 x x x ,如果 x x x 是 a a a 和 b b b 的公因数,答案就增加 x x x 。
时间复杂度 O ( V ) O(V) O ( V ) 。
60%
对于测试点#6 ,因为 a = b a=b a = b ,所以 a a a 和 b b b 的公因数等价于 a a a 的因数,找出 a a a 的所有因数再加起来就是答案了。
找 a a a 的因数时, x x x 只需要枚举到 a \sqrt{a} a ,如果 x x x 是 a a a 的因数那么 a x \frac{a}{x} x a 也是 a a a 的因数。注意判断 x = a x x=\frac{a}{x} x = x a 的情况,不要加重复了。
时间复杂度 O ( a ) O(\sqrt {a}) O ( a ) 。
结合第一部分可以拿到 60 % 60\% 60% 的分数。
100%
先求出 a a a 和 b b b 的公因数 c = gcd ( a , b ) c=\gcd(a,b) c = g cd( a , b ) , a a a 和 b b b 的公因数等价于 c c c 的因数。显然 c ≤ V c\leq V c ≤ V 。
找 c c c 的因数时,同样只需要枚举到 c \sqrt{c} c 。
时间复杂度 O ( V ) O(\sqrt {V}) O ( V ) 。
T4
30%
假设 a , b , c a,b,c a , b , c 三根木棍的长度满足 l e n a ≤ l e n b ≤ l e n c len_a\leq len_b\leq len_c l e n a ≤ l e n b ≤ l e n c ,那么当且仅当 l e n a + l e n b > l e n c len_a+len_b>len_c l e n a + l e n b > l e n c 时这三根木棍可以搭成面积大于 0 0 0 的三角形。
直接三重循环枚举三根木棍,用上述规则进行判断,符合则答案增加 1 1 1 。
注意三根木棍编号不能相同,还要避免重复枚举同一个方案。
时间复杂度 O ( n 3 ) O(n^3) O ( n 3 ) 。
50%
将木棍按照长度从短到长排序。
先用两重循环枚举更短的两根木棍 a a a 和 b b b (a < b a<b a < b ),那么只要 l e n c < l e n a + l e n b len_c<len_a+len_b l e n c < l e n a + l e n b 就可以了,用二分法求出共有多少根木棍可以和 a , b a,b a , b 一起搭成面积大于 0 0 0 的三角形。
要避免重复枚举同一方案,所以只有满足 c > b c>b c > b 才是可选的 c c c 。
时间复杂度 O ( n 2 log ( n ) ) O(n^2\log(n)) O ( n 2 log ( n )) 。
70%
对于测试点#6~7 ,预处理长度不超过 x x x 的木棍数量为 s u m [ x ] sum[x] s u m [ x ] 。
用两重循环枚举 a a a 和 b b b 之后,可选的 c c c 的数量就等于s u m [ l e n a + l e n b − 1 ] − b sum[len_a+len_b-1]-b s u m [ l e n a + l e n b − 1 ] − b 。
注意虽然 l e n len l e n 的范围是 10 6 10^6 1 0 6 ,但是 l e n a + l e n b len_a+len_b l e n a + l e n b 有可能达到 2 × 10 6 2\times 10^6 2 × 1 0 6 ,所以预处理 s u m sum s u m 数组时要预处理到 2 × 10 6 2\times 10^6 2 × 1 0 6 ,或者特判 l e n a + l e n b > 10 6 len_a+len_b>10^6 l e n a + l e n b > 1 0 6 的情况。
记 l e n i len_i l e n i 的最大值为 L = max { l e n i } L=\max\{len_i\} L = max { l e n i } 。
时间复杂度 O ( L + n 2 ) O(L+n^2) O ( L + n 2 ) 。
结合第二部分可以拿到 70 % 70\% 70% 的分数。
100%
当 a a a 不变时,随着 b b b 的增大, l e n a + l e n b len_a+len_b l e n a + l e n b 也是增大的。
所以在木棍按长度排序之后,我们可以把 b b b 和 c c c 看成双指针,当 b b b 右移时,尽量右移 c c c 直到 c = n c=n c = n 或者 l e n a + l e n b ≤ l e n c + 1 len_a+len_b\leq len_{c+1} l e n a + l e n b ≤ l e n c + 1 时停下。
相当于在第二部分的基础上,用双指针代替二分法,来求出有多少根木棍可以作为 c c c 。
时间复杂度 O ( n 2 ) O(n^2) O ( n 2 ) 。
T5
20%
枚举每个知识点学与不学,那么共有 2 n 2^n 2 n 个不同的方案,然后 O ( n ) O(n) O ( n ) 判断方案中每个选择学的知识点是否满足前置知识点都学习了,用合法方案的价值和更新答案。
时间复杂度 O ( 2 n n ) O(2^nn) O ( 2 n n ) 。
60%
因为每个知识点最多只会有一个前置知识点,可以把这类前置关系看成树的结构,p i p_i p i 是 i i i 的父节点。
用 d p [ i ] [ j ] dp[i][j] d p [ i ] [ j ] 表示以 i i i 为根的子树中,学 j j j 个知识点的最大价值。我们枚举节点 x x x 的子节点 y y y ,用 t m p [ x ] [ i ] tmp[x][i] t m p [ x ] [ i ] 表示以 x x x 为根的子树已经处理过的部分 (即不包括 y y y 等未处理的子节点和以它们为根的子树)中,学 i i i 个知识点的最大价值。那么 t m p [ x ] [ a + b ] = max { t m p [ x ] [ a ] + d p [ y ] [ b ] } tmp[x][a+b]=\max\{tmp[x][a]+dp[y][b]\} t m p [ x ] [ a + b ] = max { t m p [ x ] [ a ] + d p [ y ] [ b ]} ,这里 a a a 至少是 1 1 1 ,因为不学知识点 x x x 就无法学习子树内其他知识点。
由于可能存在多个 p i = 0 p_i=0 p i = 0 的知识点,可以把 0 0 0 看成树的根,那么答案就是 d p [ 0 ] [ m + 1 ] dp[0][m+1] d p [ 0 ] [ m + 1 ] 。
实际实现中,t m p tmp t m p 和 d p dp d p 可以是同一个数组,原理同01背包的一维写法,此时需要按 a + b a+b a + b 从大到小的顺序更新,记 s = a + b s=a+b s = a + b ,参考代码如下:
for (int s = m + 1; s >= 1; s--)
for (int b = 1; b < s; b++) {
int a = s - b;
dp[x][s] = max(dp[x][s], dp[x][a] + dp[y][b]);
}
时间复杂度 O ( n 3 ) O(n^3) O ( n 3 ) 。
100%
用 s i z [ i ] siz[i] s i z [ i ] 表示以 i i i 为根的子树中知识点数量, t m p s i z [ i ] tmpsiz[i] t m p s i z [ i ] 表示以 i i i 为根的子树已经处理过的部分 中知识点的数量。
如果限制 a ≤ t m p s i z [ x ] a\leq tmpsiz[x] a ≤ t m p s i z [ x ] 且 b ≤ s i z [ y ] b\leq siz[y] b ≤ s i z [ y ] ,可以证明总复杂度会降低到 O ( n 2 ) O(n^2) O ( n 2 ) :
这一过程的复杂度等价于在两个部分各选一个点的方案数,不难发现任意一对点在这个过程中只会在LCA(最近公共祖先)处被考虑一次,所以总复杂度是 O ( n 2 ) O(n^2) O ( n 2 ) 。
如下图,点对 ( p , q ) (p,q) ( p , q ) 仅在 L C A ( p , q ) = x LCA(p,q)=x L C A ( p , q ) = x 时被考虑,其中黄色点是以 x x x 为根的子树已经处理过的部分 ,绿色点是以 y y y 为根的子树。
参考代码如下:
for (int s = siz[x] + siz[y]; s >= 1; s--) {
for (int b = max(s - siz[x], 0); b <= siz[y] && b < s; b++) {
int a = s - b;
dp[x][s] = max(dp[x][s], dp[x][a] + dp[y][b]);
}
时间复杂度 O ( n 2 ) O(n^2) O ( n 2 ) 。
T6
10%
三重循环,外层两重循环枚举子区间 [ l , r ] [l,r] [ l , r ] ,内层再用一重循环求出区间和 s u m A ( l , r ) = ∑ i = l r A i sumA(l,r)=\sum_{i=l}^{r} A_i s u m A ( l , r ) = ∑ i = l r A i ,如果满足 a v g ( l , r ) ≥ x avg(l,r)\geq x a vg ( l , r ) ≥ x 的条件就把区间和加到答案上。
注意取模。
可以不计算平均值,用区间和直接与 x × ( r − l + 1 ) x\times (r-l+1) x × ( r − l + 1 ) 进行比较。
时间复杂度 O ( n 3 ) O(n^3) O ( n 3 ) 。
30%
预处理前缀和,就可以 O ( 1 ) O(1) O ( 1 ) 计算区间和。这样只需要两重循环枚举子区间。
时间复杂度 O ( n 2 ) O(n^2) O ( n 2 ) 。
50%
处理出数组 B i = A i − x B_i=A_i-x B i = A i − x ,记 B i B_i B i 的区间和为 s u m B ( l , r ) = ∑ i = l r B i sumB(l,r)=\sum_{i=l}^{r} B_i s u m B ( l , r ) = ∑ i = l r B i ,那么子区间 [ l , r ] [l,r] [ l , r ] 满足条件等价于 s u m B ( l , r ) ≥ 0 sumB(l,r)\geq 0 s u m B ( l , r ) ≥ 0 。
枚举子区间右端点 r r r ,对于满足 s u m B ( l , r ) ≥ 0 sumB(l,r)\geq 0 s u m B ( l , r ) ≥ 0 的每一个左端点 l l l ,答案增加 s u m A ( l , r ) sumA(l,r) s u m A ( l , r ) ,这个做法和第二部分类似。
在这个基础上,把 s u m A ( l , r ) sumA(l,r) s u m A ( l , r ) 分解为 s u m A ( 1 , r ) sumA(1,r) s u m A ( 1 , r ) 和 − s u m A ( 1 , l − 1 ) -sumA(1,l-1) − s u m A ( 1 , l − 1 ) 两部分。
s u m B ( l , r ) ≥ 0 sumB(l,r)\geq 0 s u m B ( l , r ) ≥ 0 也可以转化为 s u m B ( 1 , r ) ≥ s u m B ( 1 , l − 1 ) sumB(1,r)\geq sumB(1,l-1) s u m B ( 1 , r ) ≥ s u m B ( 1 , l − 1 ) ,称为对于 r r r 合法的 l l l 。
那么第一部分对答案贡献的次数为对于 r r r 合法的 l l l 的数量。
第二部分对答案的总贡献为每个对于 r r r 合法的 l l l 都让答案增加 − s u m A ( 1 , l − 1 ) -sumA(1,l-1) − s u m A ( 1 , l − 1 ) 。
将 B i B_i B i 的前缀和的值作为下标,用权值线段树分别维护 s u m B ( 1 , i ) = j sumB(1,i)=j s u m B ( 1 , i ) = j 对应的 i i i 的数量以及对应的 ∑ s u m A ( 1 , i − 1 ) \sum sumA(1,i-1) ∑ s u m A ( 1 , i − 1 ) ,就可以枚举 r r r ,分别计算以上两部分了。
记 B i B_i B i 的前缀和的跨度为 V = max { s u m B ( 1 , i ) } − min { s u m B ( 1 , i ) } V=\max\{sumB(1,i)\}-\min\{sumB(1,i)\} V = max { s u m B ( 1 , i )} − min { s u m B ( 1 , i )} 。
时间复杂度 O ( n log ( V ) ) O(n\log(V)) O ( n log ( V )) 。
结合第二部分可以拿到 50 % 50\% 50% 的分数。
60%
对于测试点#6 ,因为 x ≤ min { A i } x\leq \min\{A_i\} x ≤ min { A i } ,所以每个区间都满足条件。
答案就等于所有子区间的区间和之和。
时间复杂度 O ( n ) O(n) O ( n ) 。
结合第二、第三部分可以拿到 60 % 60\% 60% 的分数。
100%
在第三部分的基础上,将下标域扩展到 [ − 10 12 , 10 12 ] [-10^{12},10^{12}] [ − 1 0 12 , 1 0 12 ] 。
使用动态开点的权值线段树。
时间复杂度 O ( n log ( V ) ) O(n\log(V)) O ( n log ( V )) 。
计算出 B i B_i B i 的所有前缀和之后,进行离散化,然后以离散化后的权值为下标建线段树。
时间复杂度 O ( n log ( n ) ) O(n\log(n)) O ( n log ( n )) 。