[Codeforces] number theory (R1600) Part.9
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
1349A. Orac and LCM
原题指路:https://codeforces.com/problemset/problem/1349/A
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
给定一个长度为 n ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n (2≤n≤1e5)的序列 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 2 e 5 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 2\mathrm{e}5) a1,⋯,an (1≤ai≤2e5).求任意两下标不同的元素的 l c m \mathrm{lcm} lcm构成的集合的 gcd \gcd gcd.
思路I
设素数 p p p,则 p k ∣ a n s p^k\mid ans pk∣ans当且仅当 a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n−1)个元素是 p k p^k pk的倍数.
[证] (1)若 a [ ] a[] a[]中只有 ( n − 2 ) (n-2) (n−2)个 p k p^k pk的倍数,则 ∃ x , y s . t . p k ∤ a x , p k ∤ a y \exists x,y\ s.t.\ p^k\not\mid a_x,p^k\not\mid a_y ∃x,y s.t. pk∣ax,pk∣ay,进而 p k ∤ l c m ( a x , a y ) p^k\not\mid \mathrm{lcm}(a_x,a_y) pk∣lcm(ax,ay),则 p k ∤ a n s p^k\not\mid ans pk∣ans.
(2)若 a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n−1)个 p k p^k pk的倍数,则任意两个 a i , a j a_i,a_j ai,aj中至少有一个 p k p^k pk的倍数,显然 p k ∣ a n s p^k\mid ans pk∣ans.
设 d i d_i di是 a [ ] a[] a[]除 a i a_i ai外的元素构成的集合,则 gcd d i \gcd d_i gcddi至少能被 ( n − 1 ) (n-1) (n−1)个 a [ ] a[] a[]中的元素整除.
若 a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n−1)个元素是 p k p^k pk的倍数,则 ∃ i s . t . p k ∣ gcd d i \exists i\ s.t.\ p^k\mid \gcd d_i ∃i s.t. pk∣gcddi,
此时 a n s = l c m ( gcd ( d 1 ) , ⋯ , gcd ( d n ) ) ans=\mathrm{lcm}(\gcd(d_1),\cdots,\gcd(d_n)) ans=lcm(gcd(d1),⋯,gcd(dn)).
考察如何求 gcd d i \gcd d_i gcddi.先求前缀 gcd \gcd gcd数组 p r e i pre_i prei和后缀 gcd \gcd gcd数组 s u f i suf_i sufi,则 gcd d i = gcd ( p r e i − 1 , s u f i + 1 ) \gcd d_i=\gcd(pre_{i-1},suf_{i+1}) gcddi=gcd(prei−1,sufi+1).
代码I
const int MAXN = 1e5 + 5;
int n;
int a[MAXN];
int pre[MAXN], suf[MAXN]; // 前缀gcd、后缀gcd
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) pre[i] = gcd(pre[i - 1], a[i]);
for (int i = n; i >= 1; i--) suf[i] = gcd(suf[i + 1], a[i]);
ll ans = 1;
for (int i = 1; i <= n; i++)
ans = lcm(ans, gcd(pre[i - 1], suf[i + 1]));
cout << ans;
}
int main() {
solve();
}
1350B. Orac and Models
原题指路:https://codeforces.com/problemset/problem/1350/B
题意 ( 3 s ) (3\ \mathrm{s}) (3 s)
有编号 1 ∼ n 1\sim n 1∼n的 n n n个物品,大小分别为 s 1 , ⋯ , s n s_1,\cdots,s_n s1,⋯,sn.现要从中依次选若干个物品组成一个序列,即选中的物品编号升序.称构成的序列是好的,如果对相邻的选中物品的编号 i j i_j ij和 i j + 1 i_{j+1} ij+1,有 i j ∣ i j + 1 i_j\mid i_{j+1} ij∣ij+1,且 s i j < s i j + 1 s_{i_j}<s_{i_{j+1}} sij<sij+1.特别地,只包含一个物品的序列也是好的.求好的序列的长度的最大值.
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5).第二行输入 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之和不超过 1 e 5 1\mathrm{e}5 1e5.
思路
d p [ i ] dp[i] dp[i]表示以第 i i i个物品结尾的好的序列的长度的最大值,则 a n s = max 1 ≤ i ≤ n d p [ i ] \displaystyle ans=\max_{1\leq i\leq n}dp[i] ans=1≤i≤nmaxdp[i].
状态转移方程 d p [ i ] = max j ∣ i , s j < s i f j + 1 \displaystyle dp[i]=\max_{j\mid i,s_j<s_i} f_j +1 dp[i]=j∣i,sj<simaxfj+1.通过枚举倍数转移时间复杂度 O ( n log n ) O(n\log n) O(nlogn),通过枚举约数转移时间复杂度 O ( n n ) O(n\sqrt{n}) O(nn).
代码
const int MAXN = 1e5 + 5;
int n;
int s[MAXN];
int dp[MAXN]; // dp[i]表示以第i个物品结尾的好的序列的长度的最大值
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> s[i];
dp[i] = 1; // 清空
}
for (int i = 1; i <= n; i++) {
for (int j = 2 * i; j <= n; j += i)
if (s[i] < s[j]) dp[j] = max(dp[j], dp[i] + 1);
}
cout << *max_element(dp + 1, dp + n + 1) << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1360D. Buying Shovels
原题指路:https://codeforces.com/problemset/problem/1360/D
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据.每组测试数据输入两个整数 n , k ( 1 ≤ n , k ≤ 1 e 9 ) n,k\ \ (1\leq n,k\leq 1\mathrm{e}9) n,k (1≤n,k≤1e9).设 n n n的不大于 k k k的最大约数为 d d d,输出 n d \dfrac{n}{d} dn.
思路
①若 k ≥ n k\geq n k≥n,则 a n s = 1 ans=1 ans=1;
②若 n n n是素数,则 a n s = n ans=n ans=n.
③枚举 n n n的 ≤ n \leq\sqrt{n} ≤n的约数 d d d,检查是否有 n d ≤ k \dfrac{n}{d}\leq k dn≤k.
代码
bool check(int n) { // 判断素数
if (n <= 2) return n == 2;
for (int i = 2; (ll)i * i <= n; i++)
if (n % i == 0) return false;
return true;
}
void solve() {
int n, k; cin >> n >> k;
if (k >= n) cout << 1 << endl;
else if (check(n)) cout << n << endl;
else {
int ans = n;
for (int i = 1; (ll)i * i <= n && i <= k; i++) {
if (n % i == 0) {
if (i <= k) ans = min(ans, n / i);
if (n / i <= k) ans = min(ans, i);
}
}
cout << ans << endl;
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1370C. Number Game
原题指路:https://codeforces.com/problemset/problem/1370/C
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
Ashishgup和FastestFinger玩游戏,Ashishgup先手.初始时给定一个整数 n n n.每轮每人需进行如下两个操作之一:①设 d d d是 n n n的 > 1 >1 >1的奇约数,令 n / = d n/=d n/=d;②若 n > 1 n>1 n>1,令 n − − n-- n−−,无法操作者负.两人都采取最优策略,问谁胜.
有 t ( 1 ≤ t ≤ 100 ) t\ \ (1\leq t\leq 100) t (1≤t≤100)组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 1 e 9 ) n\ \ (1\leq n\leq 1\mathrm{e}9) n (1≤n≤1e9).
思路
(1) n n n为奇数时,
①若 n = 1 n=1 n=1,则Ashishgup第一步无法操作,FastestFinger胜.
②若 n ≥ 3 n\geq 3 n≥3,则Ashishgup第一步令 n / = n n/=n n/=n,则FastestFinger无法操作,Ashishgup胜.
(2) n n n为偶数且 n n n无 > 1 >1 >1的奇约数时,显然 n n n只能是 2 2 2的幂次.
① n = 2 n=2 n=2时,Ashishgup胜.
② n ≥ 4 n\geq 4 n≥4时,Ashishgup第一步只能令 n − − n-- n−−使其变为 ≥ 3 \geq 3 ≥3的奇数,此时FastestFinger胜.
(3) n n n为偶数且有奇约数时,
①若 4 ∣ n 4\mid n 4∣n,则Ashishgup第一步令 n n n除以其最大的奇约数使其变为 2 x ( x > 1 ) 2^x\ \ (x>1) 2x (x>1),此时Ashishgup胜.
②设 n = 2 p n=2p n=2p,其中 p p p为奇数.
i)若 p p p为素数,无论Ashishgup第一步选哪个操作都会使其变为必败态,此时FastestFinger胜.
ii)若 p p p非素数,则 p = p 1 p 2 p=p_1p_2 p=p1p2,其中 p 1 p_1 p1是素数, p 2 p_2 p2是 > 1 >1 >1的奇数.
Ashishgup第一步令 n / = p 2 n/=p_2 n/=p2,此时 n = 2 p 1 n=2p_1 n=2p1,此时Ashishgup胜.
代码
bool check(int n) { // 判断素数
if (n <= 2) return n == 2;
for (int i = 2; (ll)i * i <= n; i++)
if (n % i == 0) return false;
return true;
}
void solve() {
int n; cin >> n;
bool ok = true;
if (n == 1) ok = false;
if (n % 2 == 0 && (n & (n - 1)) == 0 && n >= 4) ok = false;
if (n % 2 == 0 && n % 4 && check(n / 2)) ok = false;
cout << (ok ? "Ashishgup" : "FastestFinger") << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1372B. Omkar and Last Class of Math
原题指路:https://codeforces.com/problemset/problem/1372/B
题意
有 t ( 1 ≤ t ≤ 10 ) t\ \ (1\leq t\leq 10) t (1≤t≤10)组测试数据.每组测试数据输入一个整数 n ( 2 ≤ n ≤ 1 e 9 ) n\ \ (2\leq n\leq 1\mathrm{e}9) n (2≤n≤1e9).求两个正整数 a , b s . t . a + b = n a,b\ s.t.\ a+b=n a,b s.t. a+b=n,且 l c m ( a , b ) \mathrm{lcm}(a,b) lcm(a,b)最小.
思路
设 n n n的最大真约数为 d d d,则取 d , n − d d,n-d d,n−d时 l c m ( d , n − d ) \mathrm{lcm}(d,n-d) lcm(d,n−d)最小.
[证] 设最优解为 k k k和 ( n − k ) (n-k) (n−k).不失一般性,不妨设 k ≤ n − k k\leq n-k k≤n−k,则 n − k ≥ n 2 n-k\geq \dfrac{n}{2} n−k≥2n.
(1)下证:若 k ∣ n k\mid n k∣n,则 l c m ( k , n − k ) = n − k < n \mathrm{lcm}(k,n-k)=n-k<n lcm(k,n−k)=n−k<n.
设 n = m k n=mk n=mk,则 n − k = ( m − 1 ) k n-k=(m-1)k n−k=(m−1)k,此时 l c m ( k , n − k ) = n − k \mathrm{lcm}(k,n-k)=n-k lcm(k,n−k)=n−k.
(2)下证:若 k ∤ n k\not\mid n k∣n,则 l c m ( k , n − k ) > n \mathrm{lcm}(k,n-k)>n lcm(k,n−k)>n.
因 l c m ( a , b ) = b \mathrm{lcm}(a,b)=b lcm(a,b)=b当且仅当 a ∣ b a\mid b a∣b,则若 k ∤ n , ( n − k ) k\not\mid n,(n-k) k∣n,(n−k),则 l c m ( k , n − k ) ≠ n − k \mathrm{lcm}(k,n-k)\neq n-k lcm(k,n−k)=n−k.
因 l c m ( k , n − k ) \mathrm{lcm}(k,n-k) lcm(k,n−k)是 ( n − k ) (n-k) (n−k)的倍数,则 l c m ( k , n − k ) ≥ 2 ( n − k ) ≥ 2 ⋅ n 2 = n \mathrm{lcm}(k,n-k)\geq 2(n-k)\geq 2\cdot\dfrac{n}{2}=n lcm(k,n−k)≥2(n−k)≥2⋅2n=n.
故为使得 l c m ( k , n − k ) \mathrm{lcm}(k,n-k) lcm(k,n−k)最小, k k k应取 n n n的非自身的最大约数.
代码
void solve() {
int n; cin >> n;
for (int i = 2; (ll)i * i <= n; i++) {
if (n % i == 0) {
int ans = n / i;
cout << ans << ' ' << n - ans << endl;
return;
}
}
cout << 1 << ' ' << n - 1 << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1379B. Dubious Cyrpto
原题指路:https://codeforces.com/problemset/problem/1379/B
题意
有 t ( 1 ≤ t ≤ 20 ) t\ \ (1\leq t\leq 20) t (1≤t≤20)组测试数据.每组测试数据输入三个整数 l , r , m ( 1 ≤ l ≤ r ≤ 5 e 5 , 1 ≤ m ≤ 1 e 10 ) l,r,m\ \ (1\leq l\leq r\leq 5\mathrm{e}5,1\leq m\leq 1\mathrm{e}10) l,r,m (1≤l≤r≤5e5,1≤m≤1e10).求三个整数 a , b , c ∈ [ l , r ] s . t . n a + b − c = m a,b,c\in[l,r]\ s.t.\ na+b-c=m a,b,c∈[l,r] s.t. na+b−c=m,输出任一组解.数据保证有解.
思路
显然可以用 O ( t ( r − l ) ) O(t(r-l)) O(t(r−l))的时间复杂度通过.
固定 b = l b=l b=l或 c = r c=r c=r.枚举 a a a,则 c − b = m % a c-b=m\% a c−b=m%a,检查答案是否在区间 [ l , r ] [l,r] [l,r]中即可.
代码
void solve() {
int l, r; ll m; cin >> l >> r >> m;
for (int a = l; a <= r; a++) {
int p = a - m % a; // c-b
if (l + p <= r) {
cout << a << ' ' << l << ' ' << l + p << endl;
return;
}
else if (r - p >= l) {
cout << a << ' ' << r - p << ' ' << r << endl;
return;
}
p = a - p;
if (l + p <= r) {
cout << a << ' ' << l + p << ' ' << l << endl;
return;
}
else if (r - p >= l) {
cout << a << ' ' << r << ' ' << r - p << endl;
return;
}
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1396A. Multiples of Length
原题指路:https://codeforces.com/problemset/problem/1396/A
题意
给定一个长度为 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5)的序列 a 1 , ⋯ , a n ( − 1 e 9 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (-1\mathrm{e}9\leq a_i\leq 1\mathrm{e}9) a1,⋯,an (−1e9≤ai≤1e9).现有操作:选择 a [ ] a[] a[]中的一个区间,区间中的每个数加上 l e n len len的某个倍数(可不同),其中 l e n len len为区间长度.构造三个操作,使得操作后 a [ ] a[] a[]的所有元素变为 0 0 0,若有多组解,输出任一组.
思路
(1) n = 1 n=1 n=1时,操作:① [ 1 , 1 ] + = 0 [1,1]+=0 [1,1]+=0;② [ 1 , 1 ] + = 0 [1,1]+=0 [1,1]+=0;③ [ 1 , 1 ] + = − a 1 [1,1]+=-a_1 [1,1]+=−a1.
(2) n ≥ 2 n\geq 2 n≥2时,操作:
① [ 1 , 1 ] + = − a 1 [1,1]+=-a_1 [1,1]+=−a1.
② [ 1 , n ] + = { 0 , − n ⋅ a 2 , − n ⋅ a 3 , ⋯ , − n ⋅ a n } [1,n]+=\{0,-n\cdot a_2,-n\cdot a_3,\cdots,-n\cdot a_n\} [1,n]+={0,−n⋅a2,−n⋅a3,⋯,−n⋅an}.
③ [ 2 , n ] + = { ( n − 1 ) ⋅ a 2 , ( n − 1 ) ⋅ a 3 , ⋯ , ( n − 1 ) ⋅ a n } [2,n]+=\{(n-1)\cdot a_2,(n-1)\cdot a_3,\cdots,(n-1)\cdot a_n\} [2,n]+={(n−1)⋅a2,(n−1)⋅a3,⋯,(n−1)⋅an}.
代码
void solve() {
int n; cin >> n;
vi a(n);
for (auto& ai : a) cin >> ai;
if (n == 1) {
cout << "1 1" << endl;
cout << 0 << endl;
cout << "1 1" << endl;
cout << 0 << endl;
cout << "1 1" << endl;
cout << -a[0] << endl;
}
else {
cout << "1 1" << endl;
cout << -a[0] << endl;
cout << "1 " << n << endl;
cout << 0 << ' ';
for (int i = 1; i < n; i++) cout << (ll)-n * a[i] << ' ';
cout << endl;
cout << "2 " << n << endl;
for (int i = 1; i < n; i++) cout << (ll)(n - 1) * a[i] << ' ';
cout << endl;
}
}
int main() {
solve();
}
1397B. Power Sequence
原题指路:https://codeforces.com/problemset/problem/1397/B
题意
称一个长度为 n n n的序列 a 0 , ⋯ , a n − 1 a_0,\cdots,a_{n-1} a0,⋯,an−1是好的,如果对 ∀ i ∈ [ 0 , n − 1 ] \forall i\in[0,n-1] ∀i∈[0,n−1],都有 a i = c i a_i=c^i ai=ci,其中 c ∈ Z + c\in\mathbb{Z}^+ c∈Z+.
给定一个长度为 n ( 3 ≤ n ≤ 1 e 5 ) n\ \ (3\leq n\leq 1\mathrm{e5}) n (3≤n≤1e5)的序列 a 0 , ⋯ , a n − 1 ( 1 ≤ a i ≤ 1 e 9 ) a_0,\cdots,a_{n-1}\ \ (1\leq a_i\leq 1\mathrm{e}9) a0,⋯,an−1 (1≤ai≤1e9).现有如下两种操作:①将序列重新排序;②选一个下标 i i i,令 a i − − a_i-- ai−−或 a i + + a_i++ ai++.求将序列 a [ ] a[] a[]变为好的序列的最小操作次数.
思路
操作①的最优策略是将 a [ ] a[] a[]按非降序排列.
[证] 将 a i a_i ai变为 c i c^i ci的代价为 ∣ a i − c i ∣ |a_i-c^i| ∣ai−ci∣.
若存在一对 ( i , j ) s . t . i < j (i,j)\ s.t.\ i<j (i,j) s.t. i<j且 a i > a j a_i>a_j ai>aj,交换 a i a_i ai和 a j a_j aj后,由 ∣ x ∣ + ∣ y ∣ = max { ∣ x + y ∣ , ∣ x − y ∣ } |x|+|y|=\max\{|x+y|,|x-y|\} ∣x∣+∣y∣=max{∣x+y∣,∣x−y∣}知:
∣ a i − c i ∣ + ∣ a j − c j ∣ = max { ∣ ( a i + a j ) − ( c i + c j ) ∣ , ∣ ( a i − a j ) − ( c i − c j ) ∣ } |a_i-c^i|+|a_j-c^j|=\max\{|(a_i+a_j)-(c^i+c^j)|,|(a_i-a_j)-(c^i-c^j)|\} ∣ai−ci∣+∣aj−cj∣=max{∣(ai+aj)−(ci+cj)∣,∣(ai−aj)−(ci−cj)∣}
≥ max { ∣ ( a j + a i ) − ( c i + c j ) ∣ , ∣ ( a j − a i ) − ( c i − c j ) ∣ } = ∣ a j − c i ∣ + ∣ a i − c j ∣ \geq \max\{|(a_j+a_i)-(c^i+c^j)|,|(a_j-a_i)-(c^i-c^j)|\}=|a_j-c^i|+|a_i-c^j| ≥max{∣(aj+ai)−(ci+cj)∣,∣(aj−ai)−(ci−cj)∣}=∣aj−ci∣+∣ai−cj∣.
因 a i > a j a_i>a_j ai>aj且 c i ≤ c j c^i\leq c^j ci≤cj,故最小步数不增,进而最优策略是将 a [ ] a[] a[]按非降序排列.
将 a [ ] a[] a[]按非降序排列,设 a m a x = a n − 1 a_{max}=a_{n-1} amax=an−1.将 a [ ] a[] a[]变为好的序列所需的步数为 f ( x ) = ∑ i = 0 n − 1 ∣ a i − x i ∣ \displaystyle f(x)=\sum_{i=0}^{n-1}|a_i-x^i| f(x)=i=0∑n−1∣ai−xi∣,其中 f ( c ) f(c) f(c)是最小步数.
注意到 c n − 1 − a m a x ≤ f ( c ) ≤ f ( 1 ) c^{n-1}-a_{max}\leq f(c)\leq f(1) cn−1−amax≤f(c)≤f(1),则 c n − 1 ≤ f ( 1 ) + a m a x c^{n-1}\leq f(1)+a_{max} cn−1≤f(1)+amax.
遍历 x = 1 , 2 , ⋯ x=1,2,\cdots x=1,2,⋯直至 x n − 1 > f ( 1 ) + a m a x x^{n-1}>f(1)+a_{max} xn−1>f(1)+amax,对每个 x x x线性地求 f ( n ) f(n) f(n),更新最小答案即可,总时间复杂度 O ( n ⋅ max ( x ) ) O(n\cdot \max(x)) O(n⋅max(x)).
这样不会TLE的原因:注意到 f ( 1 ) = ∑ i = 0 n − 1 ( a i − 1 ) < n ⋅ a m a x ≤ 1 e 9 ⋅ n \displaystyle f(1)=\sum_{i=0}^{n-1} (a_i-1)<n\cdot a_{max}\leq 1\mathrm{e}9\cdot n f(1)=i=0∑n−1(ai−1)<n⋅amax≤1e9⋅n,
则 x n − 1 ≤ f ( 1 ) + a m a x ≤ 1 e 9 ⋅ ( n + 1 ) x^{n-1}\leq f(1)+a_{max}\leq 1\mathrm{e}9\cdot (n+1) xn−1≤f(1)+amax≤1e9⋅(n+1). n = 3 , 4 , 5 , 6 n=3,4,5,6 n=3,4,5,6时, max ( x ) \max (x) max(x)分别不超过 63245 , 1709 , 278 , 93 63245,1709,278,93 63245,1709,278,93,故可过.
注意可能爆ll,但爆ll的显然不是最优解.
代码
ll add(ll a, ll b) { return a + b < INFF ? a + b : INFF; }
ll mul(ll a, ll b) { return INFF / a > b ? a * b : INFF; }
void solve() {
int n; cin >> n;
vi a(n);
for (auto& ai : a) cin >> ai;
sort(all(a));
ll ans = accumulate(all(a), (ll)0) - n;
for (int x = 1;; x++) {
ll cur = 1, res = 0;
for (int i = 0; i < n; i++) {
res = add(res, abs(a[i] - cur));
cur = mul(cur, x);
}
if (cur == INFF || cur / x > ans + a[n - 1]) break;
ans = min(ans, res);
}
cout << ans;
}
int main() {
solve();
}
1401C. Mere Array
原题指路:https://codeforces.com/problemset/problem/1401/C
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作:设序列最小元素为 m m m.选择两个下标 i , j ∈ [ 1 , n ] s . t . gcd ( a i , a j ) = m i,j\in [1,n]\ s.t.\ \gcd(a_i,a_j)=m i,j∈[1,n] s.t. gcd(ai,aj)=m,则交换 a i a_i ai和 a j a_j aj.若经若干次操作可使得 a [ ] a[] a[]非降序,则输出"YES";否则输出"NO".
有 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).
思路
显然不是 m m m的倍数的 a i a_i ai位置不能修改.
是 m m m的倍数的 a i a_i ai间可任意交换位置.
[证] 设 a x = m ; m ∣ a y , a z a_x=m;m\mid a_y,a_z ax=m;m∣ay,az.为交换 a y a_y ay和 a z a_z az,可先交换 a x a_x ax和 a y a_y ay,然后交换 a y a_y ay和 a z a_z az,最后交换 a x a_x ax和 a z a_z az.
代码
void solve() {
int n; cin >> n;
vi a(n), tmpa(n);
int m = INFF; // a[]的最小元素
for (int i = 0; i < n; i++) {
cin >> a[i];
tmpa[i] = a[i];
m = min(m, a[i]);
}
sort(all(tmpa));
for (int i = 0; i < n; i++) {
if (a[i] != tmpa[i] && a[i] % m) {
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1407B. Big Vova
原题指路:https://codeforces.com/problemset/problem/1407/B
题意
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an,将其排序为序列 b 1 , ⋯ , b n b_1,\cdots,b_n b1,⋯,bn,使得序列 c 1 , ⋯ , c n c_1,\cdots,c_n c1,⋯,cn的字典序最大,其中 c i = gcd 1 ≤ j ≤ i b j \displaystyle c_i=\gcd_{1\leq j\leq i} b_j ci=1≤j≤igcdbj.若有多组解,输出任一组.
有 t ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t (1≤t≤1000)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 1000 ) n\ \ (1\leq n\leq 1000) n (1≤n≤1000).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 1 ≤ a i ≤ 1000 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1000) a1,⋯,an (1≤ai≤1000).数据保证所有测试数据的 n n n之和不超过 1000 1000 1000.
思路I
设初始时 b [ ] b[] b[]为空,现依次将 a [ ] a[] a[]中的元素放到 b [ ] b[] b[]中.
设此时已放了 ( k − 1 ) (k-1) (k−1)个元素,现令 b k = a j s . t . c k = gcd ( b 1 , ⋯ , b k − 1 , a j ) b_k=a_j\ s.t.\ c_k=\gcd(b_1,\cdots,b_{k-1},a_j) bk=aj s.t. ck=gcd(b1,⋯,bk−1,aj)最大,显然这样 c [ ] c[] c[]的字典序最大.
事实上,解与 b k b_k bk的具体值无关,因为每个 c j c_j cj都整除 c j − 1 c_{j-1} cj−1,则 gcd ( b k , c j ) = gcd ( c k , c j ) ( j ≥ k ) \gcd(b_k,c_j)=\gcd(c_k,c_j)\ \ (j\geq k) gcd(bk,cj)=gcd(ck,cj) (j≥k).
设有备用元素 c 0 = 0 c_0=0 c0=0,则对 ∀ k ≥ 1 \forall k\geq 1 ∀k≥1,有 gcd ( 0 , k ) = k \gcd(0,k)=k gcd(0,k)=k.
做 n n n次操作,第 i i i次操作时在 a [ ] a[] a[]剩下的元素中选一个 a j s . t . g c d ( a j , c i − 1 ) a_j\ s.t.\ gcd(a_j,c_{i-1}) aj s.t. gcd(aj,ci−1)最大,令 b i = a j b_i=a_j bi=aj.
第 i i i次操作的时间复杂度为 O ( ( n − i ) log A ) O((n-i)\log A) O((n−i)logA),其中 A = max 1 ≤ i ≤ n a i \displaystyle A=\max_{1\leq i\leq n}a_i A=1≤i≤nmaxai,总时间复杂度 O ( n 2 log A ) O(n^2\log A) O(n2logA).
代码I
void solve() {
int n; cin >> n;
vi a(n);
int minidx = 0;
for (int i = 0; i < n; i++) {
cin >> a[i];
if (a[i] > a[minidx]) minidx = i;
}
vi b(n);
b[0] = a[minidx], a[minidx] = 0;
int cgcd = b[0];
for (int i = 1; i < n; i++) {
int cidx = 0, cans = 0; // 要选择的a[j]的下标j、最大c[i]
for (int j = 0; j < n; j++) {
if (a[j] && gcd(a[j], cgcd) > cans) {
cans = gcd(a[j], cgcd);
cidx = j;
}
}
b[i] = a[cidx], a[cidx] = 0;
cgcd = cans;
}
for (int i = 0; i < n; i++) cout << b[i] << " \n"[i == n - 1];
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
思路II
设序列 a [ ] a[] a[]中有 c n t x cnt_x cntx个元素为 x x x.用 c n t [ ] cnt[] cnt[],每次操作可在 O ( A log A ) O(A\log A) O(AlogA)的时间复杂度内找到最优的 b k b_k bk,总时间复杂度 O ( A n log A ) O(An\log A) O(AnlogA).
思路III
注意到对 ∀ i > 1 \forall i>1 ∀i>1,要么 c i = c i − 1 c_i=c_{i-1} ci=ci−1,要么 2 c i ≤ c i − 1 2c_i\leq c_{i-1} 2ci≤ci−1,这表明 c [ ] c[] c[]只有 O ( log A ) O(\log A) O(logA)种不同取值.
设当前已将 a [ ] a[] a[]中的 k k k个元素放入 b [ ] b[] b[]中, c k = gcd ( b 1 , ⋯ , b k ) c_k=\gcd(b_1,\cdots,b_k) ck=gcd(b1,⋯,bk).
做 O ( log A ) O(\log A) O(logA)次操作,每次操作在 a [ ] a[] a[]剩下的元素中选一个 x s . t . gcd ( c k , x ) x\ s.t.\ \gcd(c_k,x) x s.t. gcd(ck,x)最大,
将 a [ ] a[] a[]中所有值为 x x x的元素放入 b [ ] b[] b[]中.每次操作时间复杂度 O ( n log A ) O(n\log A) O(nlogA),总时间复杂度 O ( n log 2 A ) O(n\log^2 A) O(nlog2A).