[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≤1e5)n\ \ (2\leq n\leq 1\mathrm{e}5)n (2≤n≤1e5)的序列a1,⋯ ,an (1≤ai≤2e5)a_1,\cdots,a_n\ \ (1\leq a_i\leq 2\mathrm{e}5)a1,⋯,an (1≤ai≤2e5).求任意两下标不同的元素的lcm\mathrm{lcm}lcm构成的集合的gcd\gcdgcd.
思路I
设素数ppp,则pk∣ansp^k\mid anspk∣ans当且仅当a[]a[]a[]中至少有(n−1)(n-1)(n−1)个元素是pkp^kpk的倍数.
[证] (1)若a[]a[]a[]中只有(n−2)(n-2)(n−2)个pkp^kpk的倍数,则∃x,y s.t. pk∤ax,pk∤ay\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,进而pk∤lcm(ax,ay)p^k\not\mid \mathrm{lcm}(a_x,a_y)pk∣lcm(ax,ay),则pk∤ansp^k\not\mid anspk∣ans.
(2)若a[]a[]a[]中至少有(n−1)(n-1)(n−1)个pkp^kpk的倍数,则任意两个ai,aja_i,a_jai,aj中至少有一个pkp^kpk的倍数,显然pk∣ansp^k\mid anspk∣ans.
设did_idi是a[]a[]a[]除aia_iai外的元素构成的集合,则gcddi\gcd d_igcddi至少能被(n−1)(n-1)(n−1)个a[]a[]a[]中的元素整除.
若a[]a[]a[]中至少有(n−1)(n-1)(n−1)个元素是pkp^kpk的倍数,则∃i s.t. pk∣gcddi\exists i\ s.t.\ p^k\mid \gcd d_i∃i s.t. pk∣gcddi,
此时ans=lcm(gcd(d1),⋯ ,gcd(dn))ans=\mathrm{lcm}(\gcd(d_1),\cdots,\gcd(d_n))ans=lcm(gcd(d1),⋯,gcd(dn)).
考察如何求gcddi\gcd d_igcddi.先求前缀gcd\gcdgcd数组preipre_iprei和后缀gcd\gcdgcd数组sufisuf_isufi,则gcddi=gcd(prei−1,sufi+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∼n1\sim n1∼n的nnn个物品,大小分别为s1,⋯ ,sns_1,\cdots,s_ns1,⋯,sn.现要从中依次选若干个物品组成一个序列,即选中的物品编号升序.称构成的序列是好的,如果对相邻的选中物品的编号iji_jij和ij+1i_{j+1}ij+1,有ij∣ij+1i_j\mid i_{j+1}ij∣ij+1,且sij<sij+1s_{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≤1e5)n\ \ (1\leq n\leq 1\mathrm{e}5)n (1≤n≤1e5).第二行输入nnn个整数s1,⋯ ,sn (1≤si≤1e9)s_1,\cdots,s_n\ \ (1\leq s_i\leq 1\mathrm{e}9)s1,⋯,sn (1≤si≤1e9).数据保证所有测试数据的nnn之和不超过1e51\mathrm{e}51e5.
思路
dp[i]dp[i]dp[i]表示以第iii个物品结尾的好的序列的长度的最大值,则ans=max1≤i≤ndp[i]\displaystyle ans=\max_{1\leq i\leq n}dp[i]ans=1≤i≤nmaxdp[i].
状态转移方程dp[i]=maxj∣i,sj<sifj+1\displaystyle dp[i]=\max_{j\mid i,s_j<s_i} f_j +1dp[i]=j∣i,sj<simaxfj+1.通过枚举倍数转移时间复杂度O(nlogn)O(n\log n)O(nlogn),通过枚举约数转移时间复杂度O(nn)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≤1e9)n,k\ \ (1\leq n,k\leq 1\mathrm{e}9)n,k (1≤n,k≤1e9).设nnn的不大于kkk的最大约数为ddd,输出nd\dfrac{n}{d}dn.
思路
①若k≥nk\geq nk≥n,则ans=1ans=1ans=1;
②若nnn是素数,则ans=nans=nans=n.
③枚举nnn的≤n\leq\sqrt{n}≤n的约数ddd,检查是否有nd≤k\dfrac{n}{d}\leq kdn≤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先手.初始时给定一个整数nnn.每轮每人需进行如下两个操作之一:①设ddd是nnn的>1>1>1的奇约数,令n/=dn/=dn/=d;②若n>1n>1n>1,令n−−n--n−−,无法操作者负.两人都采取最优策略,问谁胜.
有t (1≤t≤100)t\ \ (1\leq t\leq 100)t (1≤t≤100)组测试数据.每组测试数据输入一个整数n (1≤n≤1e9)n\ \ (1\leq n\leq 1\mathrm{e}9)n (1≤n≤1e9).
思路
(1)nnn为奇数时,
①若n=1n=1n=1,则Ashishgup第一步无法操作,FastestFinger胜.
②若n≥3n\geq 3n≥3,则Ashishgup第一步令n/=nn/=nn/=n,则FastestFinger无法操作,Ashishgup胜.
(2)nnn为偶数且nnn无>1>1>1的奇约数时,显然nnn只能是222的幂次.
①n=2n=2n=2时,Ashishgup胜.
②n≥4n\geq 4n≥4时,Ashishgup第一步只能令n−−n--n−−使其变为≥3\geq 3≥3的奇数,此时FastestFinger胜.
(3)nnn为偶数且有奇约数时,
①若4∣n4\mid n4∣n,则Ashishgup第一步令nnn除以其最大的奇约数使其变为2x (x>1)2^x\ \ (x>1)2x (x>1),此时Ashishgup胜.
②设n=2pn=2pn=2p,其中ppp为奇数.
i)若ppp为素数,无论Ashishgup第一步选哪个操作都会使其变为必败态,此时FastestFinger胜.
ii)若ppp非素数,则p=p1p2p=p_1p_2p=p1p2,其中p1p_1p1是素数,p2p_2p2是>1>1>1的奇数.
Ashishgup第一步令n/=p2n/=p_2n/=p2,此时n=2p1n=2p_1n=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≤1e9)n\ \ (2\leq n\leq 1\mathrm{e}9)n (2≤n≤1e9).求两个正整数a,b s.t. a+b=na,b\ s.t.\ a+b=na,b s.t. a+b=n,且lcm(a,b)\mathrm{lcm}(a,b)lcm(a,b)最小.
思路
设nnn的最大真约数为ddd,则取d,n−dd,n-dd,n−d时lcm(d,n−d)\mathrm{lcm}(d,n-d)lcm(d,n−d)最小.
[证] 设最优解为kkk和(n−k)(n-k)(n−k).不失一般性,不妨设k≤n−kk\leq n-kk≤n−k,则n−k≥n2n-k\geq \dfrac{n}{2}n−k≥2n.
(1)下证:若k∣nk\mid nk∣n,则lcm(k,n−k)=n−k<n\mathrm{lcm}(k,n-k)=n-k<nlcm(k,n−k)=n−k<n.
设n=mkn=mkn=mk,则n−k=(m−1)kn-k=(m-1)kn−k=(m−1)k,此时lcm(k,n−k)=n−k\mathrm{lcm}(k,n-k)=n-klcm(k,n−k)=n−k.
(2)下证:若k∤nk\not\mid nk∣n,则lcm(k,n−k)>n\mathrm{lcm}(k,n-k)>nlcm(k,n−k)>n.
因lcm(a,b)=b\mathrm{lcm}(a,b)=blcm(a,b)=b当且仅当a∣ba\mid ba∣b,则若k∤n,(n−k)k\not\mid n,(n-k)k∣n,(n−k),则lcm(k,n−k)≠n−k\mathrm{lcm}(k,n-k)\neq n-klcm(k,n−k)=n−k.
因lcm(k,n−k)\mathrm{lcm}(k,n-k)lcm(k,n−k)是(n−k)(n-k)(n−k)的倍数,则lcm(k,n−k)≥2(n−k)≥2⋅n2=n\mathrm{lcm}(k,n-k)\geq 2(n-k)\geq 2\cdot\dfrac{n}{2}=nlcm(k,n−k)≥2(n−k)≥2⋅2n=n.
故为使得lcm(k,n−k)\mathrm{lcm}(k,n-k)lcm(k,n−k)最小,kkk应取nnn的非自身的最大约数.
代码
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≤5e5,1≤m≤1e10)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. na+b−c=ma,b,c\in[l,r]\ s.t.\ na+b-c=ma,b,c∈[l,r] s.t. na+b−c=m,输出任一组解.数据保证有解.
思路
显然可以用O(t(r−l))O(t(r-l))O(t(r−l))的时间复杂度通过.
固定b=lb=lb=l或c=rc=rc=r.枚举aaa,则c−b=m%ac-b=m\% ac−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≤1e5)n\ \ (1\leq n\leq 1\mathrm{e}5)n (1≤n≤1e5)的序列a1,⋯ ,an (−1e9≤ai≤1e9)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[]中的一个区间,区间中的每个数加上lenlenlen的某个倍数(可不同),其中lenlenlen为区间长度.构造三个操作,使得操作后a[]a[]a[]的所有元素变为000,若有多组解,输出任一组.
思路
(1)n=1n=1n=1时,操作:①[1,1]+=0[1,1]+=0[1,1]+=0;②[1,1]+=0[1,1]+=0[1,1]+=0;③[1,1]+=−a1[1,1]+=-a_1[1,1]+=−a1.
(2)n≥2n\geq 2n≥2时,操作:
①[1,1]+=−a1[1,1]+=-a_1[1,1]+=−a1.
②[1,n]+={0,−n⋅a2,−n⋅a3,⋯ ,−n⋅an}[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)⋅a2,(n−1)⋅a3,⋯ ,(n−1)⋅an}[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
题意
称一个长度为nnn的序列a0,⋯ ,an−1a_0,\cdots,a_{n-1}a0,⋯,an−1是好的,如果对∀i∈[0,n−1]\forall i\in[0,n-1]∀i∈[0,n−1],都有ai=cia_i=c^iai=ci,其中c∈Z+c\in\mathbb{Z}^+c∈Z+.
给定一个长度为n (3≤n≤1e5)n\ \ (3\leq n\leq 1\mathrm{e5})n (3≤n≤1e5)的序列a0,⋯ ,an−1 (1≤ai≤1e9)a_0,\cdots,a_{n-1}\ \ (1\leq a_i\leq 1\mathrm{e}9)a0,⋯,an−1 (1≤ai≤1e9).现有如下两种操作:①将序列重新排序;②选一个下标iii,令ai−−a_i--ai−−或ai++a_i++ai++.求将序列a[]a[]a[]变为好的序列的最小操作次数.
思路
操作①的最优策略是将a[]a[]a[]按非降序排列.
[证] 将aia_iai变为cic^ici的代价为∣ai−ci∣|a_i-c^i|∣ai−ci∣.
若存在一对(i,j) s.t. i<j(i,j)\ s.t.\ i<j(i,j) s.t. i<j且ai>aja_i>a_jai>aj,交换aia_iai和aja_jaj后,由∣x∣+∣y∣=max{∣x+y∣,∣x−y∣}|x|+|y|=\max\{|x+y|,|x-y|\}∣x∣+∣y∣=max{∣x+y∣,∣x−y∣}知:
∣ai−ci∣+∣aj−cj∣=max{∣(ai+aj)−(ci+cj)∣,∣(ai−aj)−(ci−cj)∣}|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{∣(aj+ai)−(ci+cj)∣,∣(aj−ai)−(ci−cj)∣}=∣aj−ci∣+∣ai−cj∣\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∣.
因ai>aja_i>a_jai>aj且ci≤cjc^i\leq c^jci≤cj,故最小步数不增,进而最优策略是将a[]a[]a[]按非降序排列.
将a[]a[]a[]按非降序排列,设amax=an−1a_{max}=a_{n-1}amax=an−1.将a[]a[]a[]变为好的序列所需的步数为f(x)=∑i=0n−1∣ai−xi∣\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)是最小步数.
注意到cn−1−amax≤f(c)≤f(1)c^{n-1}-a_{max}\leq f(c)\leq f(1)cn−1−amax≤f(c)≤f(1),则cn−1≤f(1)+amaxc^{n-1}\leq f(1)+a_{max}cn−1≤f(1)+amax.
遍历x=1,2,⋯x=1,2,\cdotsx=1,2,⋯直至xn−1>f(1)+amaxx^{n-1}>f(1)+a_{max}xn−1>f(1)+amax,对每个xxx线性地求f(n)f(n)f(n),更新最小答案即可,总时间复杂度O(n⋅max(x))O(n\cdot \max(x))O(n⋅max(x)).
这样不会TLE的原因:注意到f(1)=∑i=0n−1(ai−1)<n⋅amax≤1e9⋅n\displaystyle f(1)=\sum_{i=0}^{n-1} (a_i-1)<n\cdot a_{max}\leq 1\mathrm{e}9\cdot nf(1)=i=0∑n−1(ai−1)<n⋅amax≤1e9⋅n,
则xn−1≤f(1)+amax≤1e9⋅(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,6n=3,4,5,6n=3,4,5,6时,max(x)\max (x)max(x)分别不超过63245,1709,278,9363245,1709,278,9363245,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)
给定一个长度为nnn的序列a1,⋯ ,ana_1,\cdots,a_na1,⋯,an.现有操作:设序列最小元素为mmm.选择两个下标i,j∈[1,n] s.t. gcd(ai,aj)=mi,j\in [1,n]\ s.t.\ \gcd(a_i,a_j)=mi,j∈[1,n] s.t. gcd(ai,aj)=m,则交换aia_iai和aja_jaj.若经若干次操作可使得a[]a[]a[]非降序,则输出"YES";否则输出"NO".
有t (1≤t≤1e4)t\ \ (1\leq t\leq 1\mathrm{e}4)t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数n (1≤n≤1e5)n\ \ (1\leq n\leq 1\mathrm{e}5)n (1≤n≤1e5).第二行输入nnn个整数a1,⋯ ,an (1≤ai≤1e9)a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9)a1,⋯,an (1≤ai≤1e9).
思路
显然不是mmm的倍数的aia_iai位置不能修改.
是mmm的倍数的aia_iai间可任意交换位置.
[证] 设ax=m;m∣ay,aza_x=m;m\mid a_y,a_zax=m;m∣ay,az.为交换aya_yay和aza_zaz,可先交换axa_xax和aya_yay,然后交换aya_yay和aza_zaz,最后交换axa_xax和aza_zaz.
代码
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
题意
给定一个长度为nnn的序列a1,⋯ ,ana_1,\cdots,a_na1,⋯,an,将其排序为序列b1,⋯ ,bnb_1,\cdots,b_nb1,⋯,bn,使得序列c1,⋯ ,cnc_1,\cdots,c_nc1,⋯,cn的字典序最大,其中ci=gcd1≤j≤ibj\displaystyle c_i=\gcd_{1\leq j\leq i} b_jci=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).第二行输入nnn个整数a1,⋯ ,an (1≤ai≤1000)a_1,\cdots,a_n\ \ (1\leq a_i\leq 1000)a1,⋯,an (1≤ai≤1000).数据保证所有测试数据的nnn之和不超过100010001000.
思路I
设初始时b[]b[]b[]为空,现依次将a[]a[]a[]中的元素放到b[]b[]b[]中.
设此时已放了(k−1)(k-1)(k−1)个元素,现令bk=aj s.t. ck=gcd(b1,⋯ ,bk−1,aj)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[]的字典序最大.
事实上,解与bkb_kbk的具体值无关,因为每个cjc_jcj都整除cj−1c_{j-1}cj−1,则gcd(bk,cj)=gcd(ck,cj) (j≥k)\gcd(b_k,c_j)=\gcd(c_k,c_j)\ \ (j\geq k)gcd(bk,cj)=gcd(ck,cj) (j≥k).
设有备用元素c0=0c_0=0c0=0,则对∀k≥1\forall k\geq 1∀k≥1,有gcd(0,k)=k\gcd(0,k)=kgcd(0,k)=k.
做nnn次操作,第iii次操作时在a[]a[]a[]剩下的元素中选一个aj s.t. gcd(aj,ci−1)a_j\ s.t.\ gcd(a_j,c_{i-1})aj s.t. gcd(aj,ci−1)最大,令bi=ajb_i=a_jbi=aj.
第iii次操作的时间复杂度为O((n−i)logA)O((n-i)\log A)O((n−i)logA),其中A=max1≤i≤nai\displaystyle A=\max_{1\leq i\leq n}a_iA=1≤i≤nmaxai,总时间复杂度O(n2logA)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[]中有cntxcnt_xcntx个元素为xxx.用cnt[]cnt[]cnt[],每次操作可在O(AlogA)O(A\log A)O(AlogA)的时间复杂度内找到最优的bkb_kbk,总时间复杂度O(AnlogA)O(An\log A)O(AnlogA).
思路III
注意到对∀i>1\forall i>1∀i>1,要么ci=ci−1c_i=c_{i-1}ci=ci−1,要么2ci≤ci−12c_i\leq c_{i-1}2ci≤ci−1,这表明c[]c[]c[]只有O(logA)O(\log A)O(logA)种不同取值.
设当前已将a[]a[]a[]中的kkk个元素放入b[]b[]b[]中,ck=gcd(b1,⋯ ,bk)c_k=\gcd(b_1,\cdots,b_k)ck=gcd(b1,⋯,bk).
做O(logA)O(\log A)O(logA)次操作,每次操作在a[]a[]a[]剩下的元素中选一个x s.t. gcd(ck,x)x\ s.t.\ \gcd(c_k,x)x s.t. gcd(ck,x)最大,
将a[]a[]a[]中所有值为xxx的元素放入b[]b[]b[]中.每次操作时间复杂度O(nlogA)O(n\log A)O(nlogA),总时间复杂度O(nlog2A)O(n\log^2 A)O(nlog2A).