[Codeforces] number theory (R1600) Part.10
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
1423K. Lonely Numbers
原题指路:https://codeforces.com/problemset/problem/1423/K
题意 ( 1.5 s ) (1.5\ \mathrm{s}) (1.5 s)
称两个相异的正整数 a , b a,b a,b是朋友,如果 gcd ( a , b ) , a gcd ( a , b ) , b gcd ( a , b ) \gcd(a,b),\dfrac{a}{\gcd(a,b)},\dfrac{b}{\gcd(a,b)} gcd(a,b),gcd(a,b)a,gcd(a,b)b可构成非退化三角形的边长.对一个由正整数构成的集合,称其中一个元素是孤独的,如果它在该集合中无朋友.给定由 [ 1 , n ] [1,n] [1,n]的整数组成的集合,求其中孤独的数的个数.
有 t ( 1 ≤ t ≤ 1 e 6 ) t\ \ (1\leq t\leq 1\mathrm{e}6) t (1≤t≤1e6)组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n (1≤n≤1e6).
思路
(1)素数 p p p非孤独当且仅当集合中存在 p 2 p^2 p2,此时组成三角形的三边分别为 p , 1 , p p,1,p p,1,p.
(2)对合数 x = p 2 ( p ∈ p r i m e s ) x=p^2\ \ (p\in primes) x=p2 (p∈primes),因 p 2 p^2 p2在 [ 1 , n ] [1,n] [1,n]中,则 p p p也在 [ 1 , n ] [1,n] [1,n]中,故它不孤独.
(3)对合数 x ≠ p 2 ( p ∈ p r i m e s ) x\neq p^2\ \ (p\in primes) x=p2 (p∈primes),设组成三角形的三边分别为 a , b , c ( a > c ) a,b,c\ \ (a>c) a,b,c (a>c),其中 b = gcd ( a , c ) ∈ ( 1 , a ) b=\gcd(a,c)\in \left(1,\sqrt{a}\right) b=gcd(a,c)∈(1,a).
下面证明 ∃ c ∈ Z s . t . a b + b > c b , a b + c b > b , c b + b > a b \exists c\in \mathbb{Z} \ s.t.\ \dfrac{a}{b}+b>\dfrac{c}{b},\dfrac{a}{b}+\dfrac{c}{b}>b,\dfrac{c}{b}+b>\dfrac{a}{b} ∃c∈Z s.t. ba+b>bc,ba+bc>b,bc+b>ba.
①因 a > c a>c a>c,则 a b + b > c b \dfrac{a}{b}+b>\dfrac{c}{b} ba+b>bc显然成立.
②因 b < a b<\sqrt{a} b<a,则 a b + c b > a b > b \dfrac{a}{b}+\dfrac{c}{b}>\dfrac{a}{b}>b ba+bc>ba>b.
③下证 ∃ c ∈ Z s . t . a b − b < c < a b \exists c\in\mathbb{Z}\ s.t.\ \dfrac{a}{b}-b<c<\dfrac{a}{b} ∃c∈Z s.t. ba−b<c<ba.因 b > 1 b>1 b>1,则区间长度 > 1 >1 >1,故区间内至少存在一个整数,故证.
综上,一个数非孤独当且仅当它是素数且它的平方不在集合中.
设 p r e [ i ] pre[i] pre[i]为 [ 1 , i ] [1,i] [1,i]中素数的个数,则 a n s = p r e [ n ] − p r e [ ⌊ n ⌋ ] + 1 ans=pre[n]-pre\left[\left\lfloor\sqrt{n}\right\rfloor\right] + 1 ans=pre[n]−pre[⌊n⌋]+1.
代码
const int MAXN = 1e6 + 5;
int primes[MAXN], cnt;
bool state[MAXN];
int pre[MAXN]; // pre[i]表示[1,i]中素数的个数
void init() {
state[0] = state[1] = true;
for (int i = 2; i < MAXN; i++) {
if (!state[i]) primes[cnt++] = i;
for (int j = 0; primes[j] * i < MAXN; j++) {
state[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
for (int i = 1; i < MAXN; i++) pre[i] = pre[i - 1] + !state[i];
}
void solve() {
int n; cin >> n;
cout << pre[n] - pre[(uint)sqrt(n)] + 1 << endl;
}
int main() {
init();
CaseT // 单测时注释掉该行
solve();
}
1444A. Division
原题指路:https://codeforces.com/problemset/problem/1444/A
题意
有 t ( 1 ≤ t ≤ 50 ) t\ \ (1\leq t\leq50) t (1≤t≤50)组测试数据.每组测试数据给定一对整数 p , q ( 1 ≤ p ≤ 1 e 18 , 2 ≤ q ≤ 1 e 9 ) p,q\ \ (1\leq p\leq 1\mathrm{e}18,2\leq q\leq 1\mathrm{e}9) p,q (1≤p≤1e18,2≤q≤1e9),求一个最大的 x s . t . x ∣ p , q ∤ x x\ s.t.\ x\mid p,q\not\mid x x s.t. x∣p,q∣x.
思路
x ∣ p x\mid p x∣p表示 x x x的所有素因子都在 p p p中出现, q ∤ x q\not\mid x q∣x表示 q q q存在 x x x没有的素因子或 q q q与 x x x中同一素因子的次数前者 > > >后者.
因 x ∣ p x\mid p x∣p,则 x ≤ p x\leq p x≤p,而 q ≤ 1 e 9 q\leq 1\mathrm{e}9 q≤1e9,先将其素因数分解,对每个素因数 i i i,从 t m p = p tmp=p tmp=p开始一直除以 i i i直至 q ∤ t m p q\not\mid tmp q∣tmp,最终答案即 q q q的所有素因子得到的 t m p tmp tmp取 max \max max.时间复杂度 O ( p + log p ⋅ log q ) O(\sqrt{p}+\log p\cdot \log q) O(p+logp⋅logq).
代码
vi get(int n) { // 求n的素因子
vi res;
for (int i = 2; i <= n / i; i++) {
if (n % i == 0) res.push_back(i);
while (n % i == 0) n /= i;
}
if (n > 1) res.push_back(n);
return res;
}
void solve() {
ll p, q; cin >> p >> q;
if (p % q) {
cout << p << endl;
return;
}
ll ans = 1;
for (auto i : get(q)) {
ll tmp = p;
while (tmp % q == 0) tmp /= i;
ans = max(ans, tmp);
}
cout << ans << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1454D. Number into Sequence
原题指路:https://codeforces.com/problemset/problem/1454/D
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
给定一个整数 n > 1 n>1 n>1,构造一个序列 a 1 , ⋯ , a k s . t . ∏ i = 1 k a i = n , a i ∣ a i + 1 ( 1 ≤ i ≤ k − 1 ) a_1,\cdots,a_k\ s.t.\ \displaystyle \prod_{i=1}^k a_i=n,a_i\mid a_{i+1}\ \ (1\leq i\leq k-1) a1,⋯,ak s.t. i=1∏kai=n,ai∣ai+1 (1≤i≤k−1),且 k k k尽量大.若有多组解,输出任一组.
有 t ( 1 ≤ t ≤ 5000 ) t\ \ (1\leq t\leq 5000) t (1≤t≤5000)组测试数据.每组测试数据输入一个整数 n ( 2 ≤ n ≤ 1 e 10 ) n\ \ (2\leq n\leq 1\mathrm{e}10) n (2≤n≤1e10).数据保证所有测试数据的 n n n之和不超过 1 e 10 1\mathrm{e}10 1e10.
思路
设 n = ∏ i = 1 m p i α i \displaystyle n=\prod_{i=1}^m p_i^{\alpha_i} n=i=1∏mpiαi,则 k ≤ min 1 ≤ i ≤ m α i = a j \displaystyle k\leq \min_{1\leq i\leq m}\alpha_i=a_j k≤1≤i≤mminαi=aj.取 a 1 = ⋯ = a k − 1 = p j , a k = n p j k − 1 a_1=\cdots=a_{k-1}=p_j,a_k=\dfrac{n}{p_j^{k-1}} a1=⋯=ak−1=pj,ak=pjk−1n即可.
代码
vll get(ll n) { // 求n的素因数分解
vll res;
for (int i = 2; (ll)i * i <= n; i++) {
if (n % i == 0) {
int s = 0;
while (n % i == 0) n /= i, s++;
res.push_back({ s,i });
}
}
if (n > 1) res.push_back({ 1,n });
return res;
}
void solve() {
ll n; cin >> n;
auto factors = get(n);
sort(all(factors), greater<pll>());
vl ans(factors[0].first, factors[0].second);
for (int i = 1; i < factors.size(); i++)
ans.back() *= pow(factors[i].second, factors[i].first);
cout << ans.size() << endl;
for (auto i : ans) cout << i << ' ';
cout << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1458A. Row GCD
原题指路:https://codeforces.com/problemset/problem/1458/A
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个长度为 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5)的序列 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1 e 18 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}18) a1,⋯,an (1≤ai≤1e18)和一个长度为 m ( 1 ≤ m ≤ 2 e 5 ) m\ \ (1\leq m\leq 2\mathrm{e}5) m (1≤m≤2e5)的序列 b 1 , ⋯ , b m ( 1 ≤ b i ≤ 1 e 18 ) b_1,\cdots,b_m\ \ (1\leq b_i\leq 1\mathrm{e}18) b1,⋯,bm (1≤bi≤1e18).对每个 j ∈ [ 1 , m ] j\in[1,m] j∈[1,m],求 gcd ( a 1 + b j , ⋯ , a n + b j ) \gcd(a_1+b_j,\cdots,a_n+b_j) gcd(a1+bj,⋯,an+bj).
思路
注意到 gcd ( a 1 + b j , ⋯ , a n + b j ) = gcd ( a 1 + b j , a 2 − a 1 , ⋯ , a n − a 1 ) \gcd(a_1+b_j,\cdots,a_n+b_j)=\gcd(a_1+b_j,a_2-a_1,\cdots,a_n-a_1) gcd(a1+bj,⋯,an+bj)=gcd(a1+bj,a2−a1,⋯,an−a1)即可.
代码
void solve() {
int n, m; cin >> n >> m;
vl a(n), b(m);
for (auto& ai : a) cin >> ai;
for (auto& bi : b) cin >> bi;
ll g = 0;
for (int i = 1; i < n; i++) g = gcd(g, abs(a[i] - a[i - 1]));
for (int i = 0; i < m; i++) cout << gcd(g, a[0] + b[i]) << ' ';
}
int main() {
solve();
}
1462D. Add to Neighbour and Remove
原题指路:https://codeforces.com/problemset/problem/1462/D
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作,每次操作分为两步:①选择一个下标 a i a_i ai,令 a i − 1 + = a i a_{i-1}+=a_i ai−1+=ai或 a i + 1 + = a i a_{i+1}+=a_i ai+1+=ai;②移除 a i a_i ai.问至多 n n n次操作后能否使得 a [ ] a[] a[]中的元素都相等.
有 t ( 1 ≤ t ≤ 3000 ) t\ \ (1\leq t\leq 3000) t (1≤t≤3000)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 3000 ) n\ \ (1\leq n\leq 3000) n (1≤n≤3000).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1 e 5 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}5) a1,⋯,an (1≤ai≤1e5).数据保证所有测试数据的 n n n之和不超过 3000 3000 3000.
思路
因序列至多操作 ( n − 1 ) (n-1) (n−1)次,可枚举操作次数 i i i,则操作后剩下 ( n − i ) (n-i) (n−i)个元素.
注意到每次操作后序列的元素之和 s u m sum sum不变,若最后所有元素等于 k k k,则 s u m = ( n − i ) k sum=(n-i)k sum=(n−i)k.模拟操作过程,检查答案是否合法即可,时间复杂度 O ( n 2 ) O(n^2) O(n2).
代码
const int MAXN = 3005;
int n;
int a[MAXN];
bool check(int m, int k) { // 判断操作后能否剩下m个k
int sum = 0, cnt = 0; // 当前数之和、
for (int i = 0; i < n; i++) {
sum += a[i];
if (sum > k) return false; // 不合法
if (sum == k) cnt++, sum = 0; // 凑出一个k
}
return cnt == m;
}
void solve() {
cin >> n;
int sum = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum += a[i];
}
for (int i = 0; i < n; i++) { // 枚举操作次数
if (sum % (n - i) == 0 && check(n - i, sum / (n - i))) {
cout << i << endl;
return;
}
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1487D. Pythagorean Triples
原题指路:https://codeforces.com/problemset/problem/1487/D
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 1 e 9 ) n\ \ (1\leq n\leq 1\mathrm{e}9) n (1≤n≤1e9).求有多少个整数三元组 ( a , b , c ) s . t . 1 ≤ a ≤ b ≤ c ≤ n (a,b,c)\ s.t.\ 1\leq a\leq b\leq c\leq n (a,b,c) s.t. 1≤a≤b≤c≤n,且 a 2 + b 2 = c 2 , c = a 2 − b a^2+b^2=c^2,c=a^2-b a2+b2=c2,c=a2−b.
思路
c 2 = a 2 + b 2 , c = a 2 − b c^2=a^2+b^2,c=a^2-b c2=a2+b2,c=a2−b,两式相减得 b 2 + b = c 2 − c b^2+b=c^2-c b2+b=c2−c,即 b ( b + 1 ) = c ( c − 1 ) b(b+1)=c(c-1) b(b+1)=c(c−1),则 c = b + 1 c=b+1 c=b+1.
代入得 a 2 = 2 b + 1 a^2=2b+1 a2=2b+1,即每个 > 1 >1 >1的奇数 a a a对应唯一的一个 b b b,进而对应唯一的一个 c c c.枚举奇数 a a a,检查对应的 c c c是否不超过 n n n即可,时间复杂度 O ( a ) ≈ O ( c ) = O ( n ) O(a)\approx O\left(\sqrt{c}\right)=O\left(\sqrt{n}\right) O(a)≈O(c)=O(n).
代码
void solve() {
int n; cin >> n;
int ans = 0;
for (int i = 3; (ll)i * i <= 2 * n - 1; i += 2) ans++;
cout << ans << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1505B. DMCA
原题指路:https://codeforces.com/problemset/problem/1505/B
题意
给定一个整数 n ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n (1≤n≤1e6),求其digit root.
思路
设 d = n m o d 9 d=n\ \mathrm{mod}\ 9 d=n mod 9,则 n n n的digit root为 { d , d ≠ 0 9 , d = 0 \begin{cases}d,d\neq 0 \\ 9,d=0\end{cases} {d,d=09,d=0.
代码
void solve() {
int n; cin >> n;
cout << (n % 9 ? n % 9 : 9);
}
int main() {
solve();
}
1514C. Product 1 Modulo N
原题指路:https://codeforces.com/problemset/problem/1514/C
题意
给定一个整数 n ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n (2≤n≤1e5),在序列 [ 1 , ⋯ , n − 1 ] [1,\cdots,n-1] [1,⋯,n−1]中求一个最长的子列使得其中元素之积模 n n n余 1 1 1,升序输出该子列.
思路
乘积模 n n n余 1 1 1,则乘积可表示为一个数乘它模 n n n意义下的逆元.显然不能选模 n n n意义下逆元不存在或逆元不在集合中的数,而 a a a在模 n n n意义下不存在逆元当且仅当 gcd ( a , n ) ≠ 1 \gcd(a,n)\neq 1 gcd(a,n)=1,故不能选与 n n n不互素的数.
设所有与 n n n互素的数之积在模 n n n下为 p r o d prod prod.
①若 p r o d = 1 prod=1 prod=1,则这些数全选即最优解.
②若 p r o d ≠ 1 prod\neq 1 prod=1,因 gcd ( p r o d , n ) = 1 \gcd(prod,n)=1 gcd(prod,n)=1,不选值为 p r o d prod prod的数即可使得乘积在模 n n n下为 1 1 1.
代码
const int MAXN = 1e5 + 5;
int n;
bool ok[MAXN]; // 记录要选的数
void solve() {
cin >> n;
int prod = 1;
for (int i = 1; i < n; i++) {
if (gcd(i, n) == 1) {
ok[i] = true;
prod = (ll)prod * i % n;
}
}
if (prod != 1) ok[prod] = false;
cout << count(ok + 1, ok + n + 1, 1) << endl;
for (int i = 1; i < n; i++)
if (ok[i]) cout << i << ' ';
}
int main() {
solve();
}
1519C. Berland Regional
原题指路:https://codeforces.com/problemset/problem/1519/C
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
有编号 1 ∼ n 1\sim n 1∼n的 n n n所大学.有编号 1 ∼ n 1\sim n 1∼n的 n n n个学生,其中 i i i号学生在 u i u_i ui号大学,其能力值为 s i s_i si.现设定队伍的人数 k k k,每个学校将选出能力前 k k k大的人组成一支队伍,该学校的能力值为其队伍成员的能力值之和.若某学校人数不足以组成队伍,则其能力值为 0 0 0.对每个 k ∈ [ 1 , n ] k\in [1,n] k∈[1,n],求所有大学的能力值之和.
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5).第二行输入 n n n个整数 u 1 , ⋯ , u n ( 1 ≤ u i ≤ n ) u_1,\cdots,u_n\ \ (1\leq u_i\leq n) u1,⋯,un (1≤ui≤n).第三行输入 n n n个整数 s 1 , ⋯ , s n ( 1 ≤ s i ≤ 1 e 9 ) s_1,\cdots,s_n\ \ (1\leq s_i\leq 1\mathrm{e}9) s1,⋯,sn (1≤si≤1e9).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
将每个学校的学生按能力值降序排序,维护前缀和数组 p r e [ i ] [ j ] pre[i][j] pre[i][j]表示 i i i号学校的前 j j j个人的能力值之和.
设 i i i号学校人数为 x i x_i xi,则其对答案的贡献为 p r e [ i ] [ ⌊ x i k ⌋ ⋅ k ] pre[i]\left[\left\lfloor\dfrac{x_i}{k}\right\rfloor\cdot k\right] pre[i][⌊kxi⌋⋅k],其中只有 k ∈ [ 1 , x i ] k\in [1,x_i] k∈[1,xi]需计算.
代码
void solve() {
int n; cin >> n;
vi u(n), s(n);
for (auto& ui : u) {
cin >> ui;
ui--;
}
for (auto& si : s) cin >> si;
vector<vi> school(n);
for (int i = 0; i < n; i++) school[u[i]].push_back(s[i]);
for (int i = 0; i < n; i++) sort(all(school[i]), greater<int>());
vector<vl> pre(n, vl(1, 0)); // pre[i][j]表示i号学校前j个同学的能力值之和
for (int i = 0; i < n; i++)
for (auto j : school[i]) pre[i].push_back(pre[i].back() + j);
vl ans(n + 1);
for (int i = 0; i < n; i++) {
for (int k = 1; k <= school[i].size(); k++)
ans[k] += pre[i][school[i].size() / k * k];
}
for (int i = 1; i <= n; i++) cout << ans[i] << " \n"[i == n];
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1521B. Nastia and a Good Array
原题指路:https://codeforces.com/problemset/problem/1521/B
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
称一个序列是好的,如果任意两相邻元素的 gcd = 1 \gcd=1 gcd=1.给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作:选择两不同的下标 i , j ( 1 ≤ i , j ≤ n , i ≠ j ) i,j\ \ (1\leq i,j\leq n,i\neq j) i,j (1≤i,j≤n,i=j)和两个整数 x , y s . t . min { a i , a j } = min { x , y } x,y\ s.t.\ \min\{a_i,a_j\}=\min\{x,y\} x,y s.t. min{ai,aj}=min{x,y},令 a i = x , a j = y a_i=x,a_j=y ai=x,aj=y.用至多 n n n次操作将序列变为好的序列,可以证明一定有解.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (1≤ai≤1e9).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
设 x = min 1 ≤ i ≤ n a i = a p o s \displaystyle x=\min_{1\leq i\leq n}a_i=a_{pos} x=1≤i≤nminai=apos.考虑构造一个以 a p o s a_pos apos为谷底的"V字型",使得任意两相邻元素相差 1 1 1.
对每个下标 i ( 1 ≤ i ≤ n , i ≠ p o s ) i\ \ (1\leq i\leq n,i\neq pos) i (1≤i≤n,i=pos),做操作 ( p o s , i , x , x + ∣ p o s − i ∣ ) (pos,i,x,x+|pos-i|) (pos,i,x,x+∣pos−i∣),显然它符合 min { a i , a j } = min { x , y } \min\{a_i,a_j\}=\min\{x,y\} min{ai,aj}=min{x,y},其效果为令 a i = x + ∣ p o s − i ∣ a_i=x+|pos-i| ai=x+∣pos−i∣.
代码
void solve() {
int n; cin >> n;
int x = INF, pos = 0;
for (int i = 1; i <= n; i++) {
int a; cin >> a;
if (a < x) x = a, pos = i;
}
cout << (n - 1) << endl;
for (int i = 1; i <= n; i++) {
if (i != pos)
cout << pos << ' ' << i << ' ' << x << ' ' << x + abs(pos - i) << endl;
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}