CF number theory 2000 ~ 2100
目标 15 道题
1. Problem - B2 - Codeforces (再写)
给一个长度为 n ( n ≤ 1 0 6 ) n(n \le 10^6) n(n≤106) 序列, a i ≤ 1 0 6 a_i \le 10^6 ai≤106. 一次可以让一个位置(此位置的数字必须是正数)的数字减1,相邻的一个位置加1. 问最少进行多少次操作,可以存在 k > 1 k > 1 k>1,使得每个数字都可以被 k k k 整除。
注意这个质因数分解直接暴力分解没有问题。
k k k 我们只需要枚举 ∑ a i \sum a_i ∑ai 的所有质因子即可。然后计算操作数的时候,贪心一下,当前第 i i i 个位置,设 b = a i m o d p b = a_i \bmod p b=aimodp. 要么往后仍 b b b 个,要么从后面拿 p − b p - b p−b 个. 这样子处理一遍就可以了.
2. Problem - 1419E - Codeforces
给一个合数 n ( n ≤ 1 0 9 ) n(n \le 10^9) n(n≤109). 要求将 n n n 的所有大于 1 1 1 的因数排成一个环,使得相邻的两个数互质的次数尽可能的少.
这个很好构造,假设 n n n 的质因子为 p 1 , p 2 , . . . , p k p_1, p_2,...,p_k p1,p2,...,pk,那么构造成 n , p 1 , . . . , p 1 p 2 , p 2 , . . . , p 2 p 3 , p 3 . . . n, p_1, ..., p_1p_2,p_2,...,p_2p_3,p_3... n,p1,...,p1p2,p2,...,p2p3,p3... 就可以让相邻互质的数尽可能的少.
3. Problem - 1359E - Codeforces
给 n , k ( n , k ≤ 1 0 5 ) n, k(n, k \le 10^5) n,k(n,k≤105),问有多少个序列满足这样的条件: 1 ≤ a 1 < a 2 < . . . < a n ≤ k 1 \le a_1 < a_2 < ... < a_n \le k 1≤a1<a2<...<an≤k,且对于任意的 1 ∼ n 1 \sim n 1∼n 的排列 p p p,有 ( ( ( x m o d a 1 ) m o d a 2 ) … m o d a k − 1 ) m o d a k = ( ( ( x m o d a p 1 ) m o d a p 2 ) … m o d a p k − 1 ) m o d a p k (((x \bmod a_1) \bmod a_2) \dots \bmod a_{k - 1}) \bmod a_k = (((x \bmod a_{p_1}) \bmod a_{p_2}) \dots \bmod a_{p_{k - 1}}) \bmod a_{p_k} (((xmoda1)moda2)…modak−1)modak=(((xmodap1)modap2)…modapk−1)modapk.
有一个充要条件,就是满足 ( x m o d a ) m o d b = ( x m o d b ) m o d a (x \bmod a)\bmod b = (x \bmod b)\bmod a (xmoda)modb=(xmodb)moda,当且仅当 a , b a,b a,b,一个数是另一个数的倍数. 那么其实就是,对于 a 2 , a 3 , . . . , a n a_2,a_3,...,a_n a2,a3,...,an,都有 a 1 ∣ a i a_1 \mid a_i a1∣ai,那么这个序列就是成立的。答案就是 ∑ i = 1 k C k i − 1 n − 1 \sum\limits_{i=1}^{k}C_{\frac{k}{i}-1}^{n-1} i=1∑kCik−1n−1.
4. Problem - 1244C - Codeforces
给
n
,
p
,
w
,
d
(
1
≤
n
≤
1
0
12
,
0
≤
p
≤
1
0
17
,
1
≤
d
<
w
≤
1
0
5
)
n, p, w, d\ (1 \le n \le 10^{12}, 0 \le p \le 10^{17}, 1 \le d < w \le 10^{5})
n,p,w,d (1≤n≤1012,0≤p≤1017,1≤d<w≤105). 找到一组非负整数
(
x
,
y
.
z
)
(x, y. z)
(x,y.z),满足
x
⋅
w
+
y
⋅
d
=
p
x
+
y
+
z
=
n
x \cdot w + y \cdot d = p \\ x + y + z = n
x⋅w+y⋅d=px+y+z=n
用扩展欧几里得就可以求出来 a x + b y = 1 ax+by=1 ax+by=1 的一组解,然后想办法让 x + y x+y x+y 的值尽可能地小就可以了。注意中间过程会爆 long long.
5. Problem - 1210C - Codeforces
给一棵
n
n
n 个节点的树,每个点都有点权,问所有
(
u
,
v
)
(u,v)
(u,v) (保证
u
u
u 是
v
v
v 的祖先),从
u
u
u 到
v
v
v 的路径,经过节点的
gcd
\gcd
gcd 之和是多少。即求
∑
u
is an ancestor of
v
f
(
u
,
v
)
\sum_{u\text{ is an ancestor of }v} f(u, v)
u is an ancestor of v∑f(u,v)
其实深搜下去就可以了. 还是和序列一样的做法,每次新开一个 vector 储存变化的 gcd \gcd gcd 结点即可.
void dfs(int u, int fa, const vector<P>& vec)
{
vector<P> res;
for(auto p : vec)
{
p.x = __gcd(p.x, w[u]);
if(res.empty() || res.back().x != p.x) res.eb(p);
else
{
res.back().y += p.y;
}
}
res.eb(mp(w[u], 1));
for(auto p : res)
{
ans = add(ans, mul(p.x, p.y));
}
for(int i = h[u]; i != -1; i = ne[i])
{
int v = e[i];
if(v == fa) continue;
dfs(v, u, res);
}
}
6. Problem - 1184A2 - Codeforces
不会写
给一个长为
n
n
n 的 01 串
s
s
s,问有多少个
k
(
0
≤
k
<
n
)
k(0 \le k < n)
k(0≤k<n),使得存在
x
x
x,使得
x
⊕
s
h
i
f
t
k
(
x
)
=
s
x \oplus \operatorname{shift^k}(x) = s
x⊕shiftk(x)=s.
暴力判断所有长度为 gcd ( i , n ) \gcd(i,n) gcd(i,n) 的. 判断的时候,把每一个环上的数字都异或起来,只有每个环都是0的时候,才合法.
7. Problem - 1166E - Codeforces
首先是 n n n 个数,然后有 m m m 次划分,每一次输入第一部分(下标),第一部分的补给为第二部分。然后问你能否(给 a a a 数组赋值)保证每一次的第一部分的数的 lcm 严格大于第二部分的 lcm。
假设有 5 5 5 个数字,第一次输入是 1 , 2 1,2 1,2,第二次输入是 3 , 4 3,4 3,4. 那么 l c m ( 1 , 2 , 3 ) ≥ l c m ( 1 , 2 ) > l c m ( 3 , 4 , 5 ) ≥ l c m ( 3 , 4 ) lcm(1,2,3) \ge lcm(1,2) > lcm(3,4,5) \ge lcm(3,4) lcm(1,2,3)≥lcm(1,2)>lcm(3,4,5)≥lcm(3,4). 因此,如果第二次输入是 3 , 4 3,4 3,4. 则不成立.
换句话说,集合之间必须两两有交集. 这个不等价于判断所有集合的交集是否为空. 举个例子,
12
,
23
,
13
12,23,13
12,23,13 三个集合交集为空但是两两交集不为空. 因此用一个 bitset
暴力判断即可.
8. Problem - 1146D - Codeforces
一个青蛙在原点 0 0 0,可以做两个操作,一个是向右跳 a a a,一个是向左跳 b b b. f ( x ) f(x) f(x) 表示只能在 [ 0 , x ] [0,x] [0,x] 之内移动,能够到达的点的数量. 求 ∑ i = 0 m f ( i ) \sum\limits_{i=0}^{m}f(i) i=0∑mf(i).
首先当 a , b a,b a,b 互质时,若 x ≥ a + b x \ge a + b x≥a+b,则所有点都可以到达。因为 s a − t b = 1 sa - tb = 1 sa−tb=1. 一定可以在右端点不超过 a + b a+b a+b 的前提下到达 [ 0 , a − 1 ] [0,a - 1] [0,a−1] 的每一个点.
因此,前面一小段可以深搜得到, [ a + b , m ] [a+b, m] [a+b,m] 是一个等差数列。注意每一段的长度都是 gcd ( a , b ) \gcd(a,b) gcd(a,b).
void dfs(int i, int t)
{
if(i + a <= n && f[i + a] > max(f[i], i + a))
{
f[i + a] = max(f[i], i + a);
dfs(i + a, f[i + a]);
}
if(i - b >= 0 && f[i - b] >= t)
{
f[i - b] = t;
dfs(i - b, t);
}
}
9. Problem - 1101D - Codeforces
给 n ( n ≤ 2 × 1 0 5 ) n\ (n \le 2 \times 10^5) n (n≤2×105) 个结点的树,每个结点的权值是 w i ( w i ≤ 2 × 1 0 5 ) w_i\ (w_i \le 2 \times 10^5) wi (wi≤2×105). 问在 gcd > 1 \gcd > 1 gcd>1 的路径之中,路径长度最大是多少.
gcd > 1 \gcd > 1 gcd>1 当且仅当这个路径中的结点都存在相同的质因子。因此我们可以考虑枚举质因子。具体来讲,把每个节点分解质因数,然后对每个质因子,看包含此质因子的子结点可以往下延伸多少。然后最长路径就是当前节点加上两个子结点构成的路径。
包含此质因子的子结点,向下延伸的最长路径,可以用一个 map
来存储.
10. Problem - 1033D - Codeforces
给 n ( n ≤ 500 ) n\ (n \le 500) n (n≤500) 个数字 1 ≤ a i ≤ 2 × 1 0 18 1 \le a_i \le 2 \times 10^{18} 1≤ai≤2×1018,保证每个 a i a_i ai 都有 3 ∼ 5 3 \sim 5 3∼5 个因子. 问 ∏ a i \prod a_i ∏ai 的因子个数.
因为至多 500 500 500 个数字. 有四种情况,即 p q , p 2 , p 3 , p 4 pq, p^2, p^3, p^4 pq,p2,p3,p4. 后三种情况开平方,开立方就可以求,第一种情况不可以用 Pollard Rho 算法,会超时. 应该把它和其他数求一下 gcd \gcd gcd. 如果 gcd ≠ 1 \gcd \ne 1 gcd=1 且 gcd ≠ a i \gcd \ne a_i gcd=ai,那么 gcd , a i / gcd \gcd, a_i / \gcd gcd,ai/gcd 就是两个质因子. 否则 p , q p,q p,q 没有出现在其他数字中,就可以直接算进答案中而不需要具体知道 p , q p,q p,q 是什么.
注意输入重复数字的情况.
11. Problem - 1029F - Codeforces
给出a块红砖,b块蓝砖,现在用这些转拼矩形,要求两种颜色形成大矩形的同时,其中至少一种颜色的砖块也要形成矩形,问:他们所形成的所有矩形中,最小周长是多少。
div3 的题还是比 div2 的简单一些. 没啥好说的
12. Problem - 1025D - Codeforces
不会写
给 n ( n ≤ 700 ) n\ (n \le 700) n (n≤700) 个数字, a i ≤ 1 0 9 a_i \le 10^9 ai≤109. 问能否构造一棵二叉搜索树,使得树的相邻节点的 gcd > 1 \gcd > 1 gcd>1.
如果定义 d p ( l , r , r o o t ) dp(l, r, root) dp(l,r,root),表示 [ l , r ] [l,r] [l,r] 之间,以 r o o t root root 为根能否构成一棵二叉树. 这样子是一个 O ( n 4 ) O(n^4) O(n4) 的区间 d p dp dp.
不过可以这样定义, d p ( l , r , 0 ) dp(l, r, 0) dp(l,r,0) 表示 [ l , r ] [l, r] [l,r] 之间的子树可以作为 r + 1 r + 1 r+1 为根节点时的左子树, d p ( l , r , 1 ) dp(l, r, 1) dp(l,r,1) 表示 [ l , r ] [l, r] [l,r] 之间的子树可以作为 l − 1 l - 1 l−1 为根节点时的右子树. 这样子就可以变成 O ( n 3 ) O(n^3) O(n3) 的区间 d p dp dp 了.
13. Problem - 980D - Codeforces
定义一个数组的答案:将这个数组分成 k k k 组,每组里面任意一对数字乘积都是平方数, k k k 要最小。题目给出长为 n ( n ≤ 5000 ) n\ (n \le 5000) n (n≤5000) 的数组 a ( a i ≤ 1 0 8 ) a\ (a_i \le 10^8) a (ai≤108),要你求每个子数组的答案,最后输出答案为 1 , 2 , 3 , . . . , n 1,2,3,...,n 1,2,3,...,n 的子数组各有多少个.
把每个数字都变换一下,把所有质因子的幂次都模
2
2
2. 然后一个子区间可以划分的组数最小值就是这个区间变化后的数字的种类数. 这个枚举所有子区间就可以了. 但是有一个需要注意的是,最快的做法就是用 vector 离散化,然后用数组记录他们是否出现过. 这个只用了 70ms 左右. 如果在每次统计的时候开一个 unordered_map
就会超时. 也就是说 unordered_map
常数非常大.
14. Problem - 955C - Codeforces
有 Q ( Q ≤ 1 0 5 ) Q\ (Q \le 10^5) Q (Q≤105) 个询问,每次询问 [ L , R ] ( 1 ≤ L , R ≤ 1 0 18 ) [L, R]\ (1\le L, R \le 10^{18}) [L,R] (1≤L,R≤1018). 问有多少个 L ≤ x ≤ R L \le x \le R L≤x≤R,满足存在 a > 0 , p > 1 a > 0, p > 1 a>0,p>1,有 x = a p x = a^p x=ap.
注意到 p p p 最多是60,然后我就设计了带 l o g 3 n log^3n log3n 的做法,枚举 p p p 然后二分一下就可以了,然后枚举1950ms 卡过去了. 也就是找到 a p ≥ L a ^ p \ge L ap≥L 的最大的 a l a_l al,以及 a p ≤ R a^p \le R ap≤R 的最小的 a r a_r ar. 然后答案加上 μ ( p ) ∗ ( a r − a l + 1 ) \mu(p) * (a_r - a_l + 1) μ(p)∗(ar−al+1). μ ( p ) \mu(p) μ(p) 是莫尼乌斯函数,去重用的.
注意 std
中的 pow 函数浮点数误差会影响答案。可以把 pow 函数写成这个样子.
i64 pow(i64 x, int n)
{
if(std::pow(x, n) > 1e18 + 10) return INF;
i64 res = 1;
while(n)
{
if(n & 1) res = res * x;
x = x * x;
n >>= 1;
}
return res;
}
答案是把 p = 2 p = 2 p=2 和 p ≥ 3 p \ge 3 p≥3 分开处理了. 因为 p ≥ 3 p \ge 3 p≥3 的时候, a p a^p ap 中的 a a a 不超过 1 0 6 10^6 106 因此可以提前把这些数字处理出来,然后二分查找,注意处理的时候要跳过完全平方数. p = 2 p=2 p=2 的时候也二分查找即可.
15. Problem - 920F - Codeforces
给一个 长为 n ( n ≤ 3 × 1 0 5 ) n\ (n \le 3 \times 10^5) n (n≤3×105) 的数组,给 q ( q ≤ 3 × 1 0 5 ) q\ (q \le 3 \times 10^5) q (q≤3×105) 组操作,操作分两种,一个是把 [ l , r ] [l,r] [l,r] 之间的数字 a i = D ( a i ) a_i = D(a_i) ai=D(ai), D ( x ) D(x) D(x) 就是 x x x 的因子个数;另一种是区间和.
线段树裸题了。不过一定要小心 D ( 1 ) = 1 , D ( 2 ) = 2 D(1) = 1,\ D(2) = 2 D(1)=1, D(2)=2. 因此不变化的点有两个, 1 , 2 1,2 1,2 都要特判.
到此为止不写了