[Codeforces] number theory (R1600) Part.9

[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  (2n1e5)的序列 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  (1ai2e5).求任意两下标不同的元素的 l c m \mathrm{lcm} lcm构成的集合的 gcd ⁡ \gcd gcd.

思路I

设素数 p p p,则 p k ∣ a n s p^k\mid ans pkans当且仅当 a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n1)个元素是 p k p^k pk的倍数.

[] (1)若 a [ ] a[] a[]中只有 ( n − 2 ) (n-2) (n2) 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. pkax,pkay,进而 p k ∤ l c m ( a x , a y ) p^k\not\mid \mathrm{lcm}(a_x,a_y) pklcm(ax,ay),则 p k ∤ a n s p^k\not\mid ans pkans.

(2)若 a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n1) 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 pkans.

d i d_i di a [ ] a[] a[] a i a_i ai外的元素构成的集合,则 gcd ⁡ d i \gcd d_i gcddi至少能被 ( n − 1 ) (n-1) (n1) a [ ] a[] a[]中的元素整除.

a [ ] a[] a[]中至少有 ( n − 1 ) (n-1) (n1)个元素是 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. pkgcddi,

​ 此时 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(prei1,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 1n 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} ijij+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  (1t100)组测试数据.每组测试数据第一行输入一个整数 n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5).第二行输入 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  (1si1e9).数据保证所有测试数据的 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=1inmaxdp[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]=ji,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  (1t100)组测试数据.每组测试数据输入两个整数 n , k    ( 1 ≤ n , k ≤ 1 e 9 ) n,k\ \ (1\leq n,k\leq 1\mathrm{e}9) n,k  (1n,k1e9).设 n n n的不大于 k k k的最大约数为 d d d,输出 n d \dfrac{n}{d} dn.

思路

①若 k ≥ n k\geq n kn,则 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 dnk.

代码

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  (1t100)组测试数据.每组测试数据输入一个整数 n    ( 1 ≤ n ≤ 1 e 9 ) n\ \ (1\leq n\leq 1\mathrm{e}9) n  (1n1e9).

思路

(1) n n n为奇数时,

​ ①若 n = 1 n=1 n=1,则Ashishgup第一步无法操作,FastestFinger胜.

​ ②若 n ≥ 3 n\geq 3 n3,则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 n4时,Ashishgup第一步只能令 n − − n-- n使其变为 ≥ 3 \geq 3 3的奇数,此时FastestFinger胜.

(3) n n n为偶数且有奇约数时,

​ ①若 4 ∣ n 4\mid n 4n,则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  (1t10)组测试数据.每组测试数据输入一个整数 n    ( 2 ≤ n ≤ 1 e 9 ) n\ \ (2\leq n\leq 1\mathrm{e}9) n  (2n1e9).求两个正整数 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,nd l c m ( d , n − d ) \mathrm{lcm}(d,n-d) lcm(d,nd)最小.

[] 设最优解为 k k k ( n − k ) (n-k) (nk).不失一般性,不妨设 k ≤ n − k k\leq n-k knk,则 n − k ≥ n 2 n-k\geq \dfrac{n}{2} nk2n.

(1)下证:若 k ∣ n k\mid n kn,则 l c m ( k , n − k ) = n − k < n \mathrm{lcm}(k,n-k)=n-k<n lcm(k,nk)=nk<n.

​ 设 n = m k n=mk n=mk,则 n − k = ( m − 1 ) k n-k=(m-1)k nk=(m1)k,此时 l c m ( k , n − k ) = n − k \mathrm{lcm}(k,n-k)=n-k lcm(k,nk)=nk.

(2)下证:若 k ∤ n k\not\mid n kn,则 l c m ( k , n − k ) > n \mathrm{lcm}(k,n-k)>n lcm(k,nk)>n.

​ 因 l c m ( a , b ) = b \mathrm{lcm}(a,b)=b lcm(a,b)=b当且仅当 a ∣ b a\mid b ab,则若 k ∤ n , ( n − k ) k\not\mid n,(n-k) kn,(nk),则 l c m ( k , n − k ) ≠ n − k \mathrm{lcm}(k,n-k)\neq n-k lcm(k,nk)=nk.

​ 因 l c m ( k , n − k ) \mathrm{lcm}(k,n-k) lcm(k,nk) ( n − k ) (n-k) (nk)的倍数,则 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,nk)2(nk)22n=n.

故为使得 l c m ( k , n − k ) \mathrm{lcm}(k,n-k) lcm(k,nk)最小, 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  (1t20)组测试数据.每组测试数据输入三个整数 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  (1lr5e5,1m1e10).求三个整数 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+bc=m,输出任一组解.数据保证有解.

思路

显然可以用 O ( t ( r − l ) ) O(t(r-l)) O(t(rl))的时间复杂度通过.

固定 b = l b=l b=l c = r c=r c=r.枚举 a a a,则 c − b = m % a c-b=m\% a cb=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  (1n1e5)的序列 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  (1e9ai1e9).现有操作:选择 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 n2时,操作:

​ ① [ 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,na2,na3,,nan}.

​ ③ [ 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]+={(n1)a2,(n1)a3,,(n1)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,,an1是好的,如果对 ∀ i ∈ [ 0 , n − 1 ] \forall i\in[0,n-1] i[0,n1],都有 a i = c i a_i=c^i ai=ci,其中 c ∈ Z + c\in\mathbb{Z}^+ cZ+.

给定一个长度为 n    ( 3 ≤ n ≤ 1 e 5 ) n\ \ (3\leq n\leq 1\mathrm{e5}) n  (3n1e5)的序列 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,,an1  (1ai1e9).现有如下两种操作:①将序列重新排序;②选一个下标 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| aici.

若存在一对 ( 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,xy}知:

∣ 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)|\} aici+ajcj=max{(ai+aj)(ci+cj),(aiaj)(cicj)}

≥ 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),(ajai)(cicj)}=ajci+aicj.

a i > a j a_i>a_j ai>aj c i ≤ c j c^i\leq c^j cicj,故最小步数不增,进而最优策略是将 a [ ] a[] a[]按非降序排列.

a [ ] a[] a[]按非降序排列,设 a m a x = a n − 1 a_{max}=a_{n-1} amax=an1.将 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=0n1aixi,其中 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) cn1amaxf(c)f(1),则 c n − 1 ≤ f ( 1 ) + a m a x c^{n-1}\leq f(1)+a_{max} cn1f(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} xn1>f(1)+amax,对每个 x x x线性地求 f ( n ) f(n) f(n),更新最小答案即可,总时间复杂度 O ( n ⋅ max ⁡ ( x ) ) O(n\cdot \max(x)) O(nmax(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=0n1(ai1)<namax1e9n,

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) xn1f(1)+amax1e9(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  (1t1e4)组测试数据.每组测试数据第一行输入一个整数 n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5).第二行输入 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  (1ai1e9).

思路

显然不是 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;may,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=1jigcdbj.若有多组解,输出任一组.

t    ( 1 ≤ t ≤ 1000 ) t\ \ (1\leq t\leq 1000) t  (1t1000)组测试数据.每组测试数据第一行输入一个整数 n    ( 1 ≤ n ≤ 1000 ) n\ \ (1\leq n\leq 1000) n  (1n1000).第二行输入 n n n个整数 a 1 , ⋯   , a n    ( 1 ≤ a i ≤ 1000 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1000) a1,,an  (1ai1000).数据保证所有测试数据的 n n n之和不超过 1000 1000 1000.

思路I

设初始时 b [ ] b[] b[]为空,现依次将 a [ ] a[] a[]中的元素放到 b [ ] b[] b[]中.

设此时已放了 ( k − 1 ) (k-1) (k1)个元素,现令 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,,bk1,aj)最大,显然这样 c [ ] c[] c[]的字典序最大.

事实上,解与 b k b_k bk的具体值无关,因为每个 c j c_j cj都整除 c j − 1 c_{j-1} cj1,则 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)  (jk).

设有备用元素 c 0 = 0 c_0=0 c0=0,则对 ∀ k ≥ 1 \forall k\geq 1 k1,有 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,ci1)最大,令 b i = a j b_i=a_j bi=aj.

i i i次操作的时间复杂度为 O ( ( n − i ) log ⁡ A ) O((n-i)\log A) O((ni)logA),其中 A = max ⁡ 1 ≤ i ≤ n a i \displaystyle A=\max_{1\leq i\leq n}a_i A=1inmaxai,总时间复杂度 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=ci1,要么 2 c i ≤ c i − 1 2c_i\leq c_{i-1} 2cici1,这表明 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).



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值