[Codeforces] number theory (R1900) Part.2

2023-10-28

[Codeforces] number theory (R1900) Part.2

题单:https://codeforces.com/problemset/page/1?tags=number%20theory,1601-1900

294C. Shaass and Lights

原题指路:https://codeforces.com/problemset/problem/294/C

题意

n n n盏灯排成一行,从左到右依次编号 1 ∼ n 1\sim n 1n,初始时有些灯亮有些灯灭.每一步可点亮个灭了的灯,如果它两边至少有一个亮的灯.问有多少种使得灯全亮的开灯方案,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.

第一行输入两个整数 n , m    ( 1 ≤ m ≤ n ≤ 1000 ) n,m\ \ (1\leq m\leq n\leq 1000) n,m  (1mn1000),分别表示灯的个数和初始时亮的灯的个数.第二行输入 n n n个范围为 [ 1 , n ] [1,n] [1,n]的整数,表示初始时亮的灯的编号.

思路

m m m个亮的灯将序列分为 ( m + 1 ) (m+1) (m+1)段,从左往右编号 1 ∼ ( m + 1 ) 1\sim (m+1) 1(m+1).每次操作只能点亮每一段内的最左边或最右边的灯,特别地,第一段只能点亮最右边的灯,最后一段只能点亮最左边的灯.

设第 i    ( 1 ≤ i ≤ m + 1 ) i\ \ (1\leq i\leq m+1) i  (1im+1)段的长度为 l e n i len_i leni,先只考虑每次点亮的是哪一段内的灯,有 ( n − m ) ! ∏ i = 1 m + 1 l e n i ! \dfrac{(n-m)!}{\displaystyle\prod_{i=1}^{m+1} len_i!} i=1m+1leni!(nm)!种情况.

考虑每次点亮每一段内的最左边还是最右边的灯,第 i i i段中前 ( l i − 1 ) (l_i-1) (li1)个灯都有两种选择,最后一个灯只有一种选择,故 a n s = ( n − m ) ! ∏ i = 1 m + 1 l e n i ! ⋅ ∏ i = 1 m + 1 2 l e n i − 1 \displaystyle ans=\dfrac{(n-m)!}{\displaystyle\prod_{i=1}^{m+1} len_i!}\cdot \prod_{i=1}^{m+1} 2^{len_i-1} ans=i=1m+1leni!(nm)!i=1m+12leni1.

代码

const int MAXN = 1005;
const int MOD = 1e9 + 7;
int n, m;
int pos[MAXN];
int fac[MAXN], ifac[MAXN];
int pow2[MAXN];

void init() {
  fac[0] = 1;
  for (int i = 1; i < MAXN; i++) fac[i] = (ll)fac[i - 1] * i % MOD;

  ifac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2, MOD);
  for (int i = MAXN - 1; i; i--) ifac[i - 1] = (ll)ifac[i] * i % MOD;

  pow2[0] = 1;
  for (int i = 1; i < MAXN; i++) pow2[i] = (ll)pow2[i - 1] * 2 % MOD;
}

void solve() {
  init();

  cin >> n >> m;
  for (int i = 1; i <= m; i++) cin >> pos[i];

  sort(pos + 1, pos + m + 1);
  pos[m + 1] = n + 1;  // 哨兵,减少特判边界

  int ans = fac[n - m];
  for (int i = 0; i <= m; i++) {
    int len = pos[i + 1] - pos[i] - 1;
    if (len <= 0) continue;

    if (i > 0 && i < m)  // 注意pos[i]和pos[i+1]都在范围内时才需要乘2的幂次
      ans = (ll)ans * pow2[len - 1] % MOD;
    ans = (ll)ans * ifac[len] % MOD;
  }
  cout << ans;
}

int main() {
  solve();
}


336C. Vasily the Bear and Sequence

原题指路:https://codeforces.com/problemset/problem/336/C

题意

一个集合 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an的权值定义为最大的非负整数 v   s . t .   2 v ∣ ( a 1 & ⋯ & a n ) v\ s.t.\ 2^v\mid (a_1\&\cdots\& a_n) v s.t. 2v(a1&&an),若这样的非负整数 v v v不存在,定义该集合的权值为 − 1 -1 1.给定 n n n个整数 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an,从中选出 k k k个相异的整数$b_1,\cdots,b_k\ s.t.\ 它们的构成的集合权值最大 . 若有多组解 , 输出 它们的构成的集合权值最大.若有多组解,输出 它们的构成的集合权值最大.若有多组解,输出k$最大的任一组.

第一行输入一个整数 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).

思路

x % y ≤ min ⁡ { x , y } x\% y\leq \min\{x,y\} x%ymin{x,y},从高到低枚举 v v v,将所有二进制第 v v v位为 1 1 1的元素存下来,检查它们相与后能否被 2 v 2^v 2v整除即可.枚举完所有 v v v即无解.

代码

void solve() {
  int n; cin >> n;
  vi a(n);
  for (auto& ai : a) cin >> ai;

  vi ans;
  for (int v = 30; v >= 0; v--) {
    ans.clear();
    int tmp = 0x3fffffff;  // 选出来的数相与的结果
    for (auto ai : a) {
      if (ai >> v & 1) {
        ans.push_back(ai);
        tmp &= ai;
      }
    }

    if (ans.size() && tmp % (1 << v) == 0) {
      cout << ans.size() << endl;
      for (auto i : ans) cout << i << ' ';
      return;
    }
  }
  cout << -1;
}

int main() {
  solve();
}


359C. Prime Number

原题指路:https://codeforces.com/problemset/problem/359/C

题意

给定一个素数 x    ( 2 ≤ x ≤ 1 e 9 ) x\ \ (2\leq x\leq 1\mathrm{e}9) x  (2x1e9) n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5)个非负整数 a 1 , ⋯   , a n    ( 0 ≤ a 1 ≤ ⋯ ≤ a n ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (0\leq a_1\leq \cdots\leq a_n\leq 1\mathrm{e}9) a1,,an  (0a1an1e9).设 1 x a 1 + ⋯ + 1 x a n = s t \dfrac{1}{x^{a_1}}+\cdots+\dfrac{1}{x^{a_n}}=\dfrac{s}{t} xa11++xan1=ts,其中 t = x a 1 + ⋯ + a n t=x^{a_1+\cdots+a_n} t=xa1++an.求 gcd ⁡ ( s , t ) \gcd(s,t) gcd(s,t),答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.

思路I

s u m = a 1 + ⋯ + a n sum=a_1+\cdots+a_n sum=a1++an,则 s = x s u m − a 1 + ⋯ + x s u m − a n s=x^{sum-a_1}+\cdots+x^{sum-a_n} s=xsuma1++xsuman.显然 a n s = x k    ( k ∈ Z ) ans=x^k\ \ (k\in\mathbb{Z}) ans=xk  (kZ).

下面求 k k k.

b i = s u m − a i b_i=sum-a_i bi=sumai,则 b 1 ≥ ⋯ ≥ b n b_1\geq\cdots\geq b_n b1bn.每次取出 b [ ] b[] b[]中最小的元素 v v v,统计 b [ ] b[] b[]中值为 v v v的元素个数 c n t cnt cnt.

①若 c n t cnt cnt不是 x x x的倍数,则 a n s = x min ⁡ { v , s u m } ans=x^{\min\{v,sum\}} ans=xmin{v,sum}.

②若 c n t cnt cnt x x x的倍数,则此时的 v v v可能仍不是答案,在 b [ ] b[] b[]末尾加入 c n t x \dfrac{cnt}{x} xcnt ( v + 1 ) (v+1) (v+1).重复上述过程

代码I

const int MOD = 1e9 + 7;

void solve() {
  int n, x; cin >> n >> x;
  vi a(n);
  ll sum = 0;  // a[]的元素之和
  for (auto& ai : a) {
    cin >> ai;
    sum += ai;
  }

  vl b(n);
  for (int i = 0; i < n; i++) b[i] = sum - a[i];

  while (true) {
    ll v = b.back();
    int cnt = 0;  // b[]中值为v的数的个数
    while (b.size() && b.back() == v) {
      cnt++;
      b.pop_back();
    }

    if (cnt % x) {
      cout << qpow(x, min(v, sum), MOD);
      return;
    }
    else {
      cnt /= x;
      while (cnt--) b.push_back(v + 1);
    }
  }
}

int main() {
  solve();
}

思路II

s s s t t t x x x进制表示中末尾连续 0 0 0的个数分别为 c 1 c_1 c1 c 2 c_2 c2,则 gcd ⁡ ( s , t ) = x min ⁡ { c 1 , c 2 } \gcd(s,t)=x^{\min\{c_1,c_2\}} gcd(s,t)=xmin{c1,c2}.

用一个map来模拟将每个 ( s u m − a i ) (sum-a_i) (sumai)转成 x x x进制数的过程即可.

代码II

const int MOD = 1e9 + 7;
int n, x;
map<ll, int> cnt;  // 记录每个(sum - a[i])出现的次数

void solve() {
  cin >> n >> x;
  vi a(n);
  ll sum = 0;  // a[]的元素之和
  for (auto& ai : a) {
    cin >> ai;
    sum += ai;
  }

  for (auto ai : a) cnt[sum - ai]++;

  for (auto& [u, v] : cnt)  // 注意取引用
    if (v >= x) cnt[u + 1] += v / x, v %= x;  // 处理x进制的进位

  for (auto [u, v] : cnt) {
    if (v) {
      cout << qpow(x, min(sum, u), MOD);
      return;
    }
  }
}

int main() {
  solve();
}


385C. Bear and Prime Numbers

原题指路:https://codeforces.com/problemset/problem/385/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个询问.每个询问给定两个整数 l , r l,r l,r,求 ∑ p ∈ S ( l , r ) f ( p ) \displaystyle \sum_{p\in S(l,r)} f(p) pS(l,r)f(p),其中 S ( l , r ) S(l,r) S(l,r)表示 [ l , r ] [l,r] [l,r]范围内的素数, f ( p ) f(p) f(p)表示使得 p ∣ a k    ( l ≤ k ≤ r ) p\mid a_k\ \ (l\leq k\leq r) pak  (lkr)的下标 k k k的个数.

第一行输入一个整数 n    ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n  (1n1e6).第二行输入 n n n个整数 a 1 , ⋯   , a n    ( 2 ≤ a i ≤ 1 e 7 ) a_1,\cdots,a_n\ \ (2\leq a_i\leq 1\mathrm{e}7) a1,,an  (2ai1e7).第三行输入一个整数 m    ( 1 ≤ m ≤ 5 e 4 ) m\ \ (1\leq m\leq 5\mathrm{e}4) m  (1m5e4),表示询问次数.接下来 m m m行每行输入两个整数 l , r    ( 2 ≤ l ≤ r ≤ 2 e 9 ) l,r\ \ (2\leq l\leq r\leq 2\mathrm{e}9) l,r  (2lr2e9).

思路

a ≤ 1 e 7 a\leq 1\mathrm{e}7 a1e7,则只需考虑 1 e 7 1\mathrm{e}7 1e7内的素数对答案的贡献,换句话说,只需对每个 1 e 7 1\mathrm{e}7 1e7内的素数 p p p,求 f ( p ) f(p) f(p).

考虑如何求 f ( p ) f(p) f(p).设 c n t [ x ] cnt[x] cnt[x]表示 a [ ] a[] a[]中元素 x x x出现的次数.

f ( 2 ) = c n t [ 1 ⋅ 2 ] + c n t [ 2 ⋅ 2 ] + ⋯ f(2)=cnt[1\cdot 2]+cnt[2\cdot 2]+\cdots f(2)=cnt[12]+cnt[22]+.

f ( 3 ) = c n t [ 1 ⋅ 3 ] + c n t [ 2 ⋅ 3 ] + ⋯ f(3)=cnt[1\cdot 3]+cnt[2\cdot 3]+\cdots f(3)=cnt[13]+cnt[23]+.

f ( p ) = c n t [ 1 ⋅ p ] + c n t [ 2 ⋅ p ] + ⋯ f(p)=cnt[1\cdot p]+cnt[2\cdot p]+\cdots f(p)=cnt[1p]+cnt[2p]+.

显然上述过程类似于埃氏筛,可在埃氏筛筛素数的过程中预处理出 f ( p ) f(p) f(p).

f ( p ) f(p) f(p)的前缀和数组 p r e [ ] pre[] pre[],对每个询问 [ l , r ] [l,r] [l,r],二分出在范围 [ l , r ] [l,r] [l,r]内的最小、最大的素数在素数数组 p r i m e s [ ] primes[] primes[]中的下标 L , R L,R L,R,则 a n s = p r e [ r ] − p r e [ l − 1 ] ans=pre[r]-pre[l-1] ans=pre[r]pre[l1].注意 l , r l,r l,r可能超过 1 e 7 1\mathrm{e}7 1e7,而超过 1 e 7 1\mathrm{e}7 1e7的部分对答案无贡献,故 l > 1 e 7 l>1\mathrm{e}7 l>1e7内最大的素数时直接返回 0 0 0即可.

代码

const int MAXN = 1e7 + 5;
int n;
int mp[MAXN];  // mp[x]表示a[]中值为x的数的个数
int primes[MAXN], cnt;
bool state[MAXN];
int f[MAXN];  // f[]的前缀和数组

void init() {  // 预处理f[]
  for (int i = 2; i < MAXN; i++) {
    if (!state[i]) primes[cnt++] = i;

    for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
      state[primes[j] * i] = true;
      if (i % primes[j] == 0) break;
    }
  }

  for (int i = 0; i < cnt; i++) {
    for (int j = 1; (ll)primes[i] * j < MAXN; j++)
      f[i] += mp[primes[i] * j];
    f[i] += f[i - 1];  // 求前缀和
  }
}

void solve() {
  cin >> n;
  while (n--) {
    int a; cin >> a;
    mp[a]++;
  }

  init();

  CaseT{
    int l, r; cin >> l >> r;
    if (l > primes[cnt - 1]) {
      cout << 0 << endl;
      continue;
    }

    int L = lower_bound(primes, primes + cnt, l) - primes;
    int R = upper_bound(primes, primes + cnt, r) - primes - 1;
    cout << f[R] - f[L - 1] << endl;
  }
}

int main() {
  solve();
}


402D. Upgrading Array

原题指路:https://codeforces.com/problemset/problem/402/D

题意

给定一个长度为 n n n的整数序列 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an和一个坏素数的集合 b 1 , ⋯   , b m b_1,\cdots,b_m b1,,bm.未出现在 b [ ] b[] b[]中的素数称为好素数.定义序列 a [ ] a[] a[]的权值未 ∑ i = 1 n f ( a i ) \displaystyle \sum_{i=1}^n f(a_i) i=1nf(ai),其中 f ( s ) f(s) f(s)定义如下:① f ( 1 ) = 0 f(1)=0 f(1)=0;②设 p p p s s s的最小素因子.若 p p p是好素数, f ( s ) = f ( s p ) + 1 f(s)=f\left(\dfrac{s}{p}\right)+1 f(s)=f(ps)+1;否则 f ( s ) = f ( s p ) − 1 f(s)=f\left(\dfrac{s}{p}\right)-1 f(s)=f(ps)1.现有操作:选择一个下标 r    ( 1 ≤ r ≤ n ) r\ \ (1\leq r\leq n) r  (1rn),设 g = gcd ⁡ ( a 1 , ⋯   , a r ) g=\gcd(a_1,\cdots,a_r) g=gcd(a1,,ar),令 a 1 / = g , ⋯   , a r / = g a_1/=g,\cdots,a_r/=g a1/=g,,ar/=g.问进行若干次(可能为零次)操作后序列 a [ ] a[] a[]的最大权值.

第一行输入两个整数 n , m    ( 1 ≤ n , m ≤ 5000 ) n,m\ \ (1\leq n,m\leq 5000) n,m  (1n,m5000).第二行输入 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个整数 b 1 , ⋯   , b m    ( 2 ≤ b 1 < ⋯ < b m ≤ 1 e 9 ) b_1,\cdots,b_m\ \ (2\leq b_1<\cdots<b_m\leq 1\mathrm{e}9) b1,,bm  (2b1<<bm1e9).

思路I

注意到前缀 gcd ⁡ \gcd gcd p r e r = gcd ⁡ 1 ≤ i ≤ r a i \displaystyle pre_r=\gcd_{1\leq i\leq r} a_i prer=1irgcdai r r r的增大不增,且对 ∀ r ′ < r \forall r'<r r<r, p r e r pre_{r} prer的素因子是 p r e r ′ pre_{r'} prer的素因子的子集.若从前往后考虑每个操作,对每个操作 i    ( 1 ≤ i ≤ n ) i\ \ (1\leq i\leq n) i  (1in),只有第一次操作时才会产生贡献 − i ⋅ f ( gcd ⁡ ( a 1 , ⋯   , a i ) ) -i\cdot f(\gcd(a_1,\cdots,a_i)) if(gcd(a1,,ai)).对每个操作 j    ( i < j ≤ n ) j\ \ (i<j\leq n) j  (i<jn),因操作 i i i使得前 i i i个数互素,则前 j j j个数的 gcd ⁡ = 1 \gcd=1 gcd=1,不再对答案有贡献.故从前往后考虑每个操作相当于只进行了第一次操作.

从后往前考虑每个操作,设当前操作为 i i i,上一操作为 j j j,则当前位置剩余的最小素因子的集合等于 gcd ⁡ ( a 1 , ⋯   , a i ) \gcd(a_1,\cdots,a_i) gcd(a1,,ai)的素因子构成的集合与 gcd ⁡ ( a 1 , ⋯   , a j ) \gcd(a_1,\cdots,a_j) gcd(a1,,aj)的素因子构成的集合的差集,但这不方便统计答案.从前往后考察上述过程,因前缀 gcd ⁡ \gcd gcd不增,则前面的操作不会影响当前位置剩余的素因子的集合,只影响每个素因子的贡献的倍数.设当前操作为 i i i,上一操作为 j j j,则操作 i i i对答案的贡献为 − ( i − j ) ⋅ f ( gcd ⁡ ( a 1 , ⋯   , a i ) ) -(i-j)\cdot f(\gcd(a_1,\cdots,a_i)) (ij)f(gcd(a1,,ai)).

d p [ i ] dp[i] dp[i]表示操作完前 i i i个数后增加的分数的最大值,则 a n s = ∑ i = 1 n f ( a i ) + d p n \displaystyle ans=\sum_{i=1}^n f(a_i)+dp_n ans=i=1nf(ai)+dpn.

状态转移方程 d p [ i ] = max ⁡ 0 ≤ j < i { d p [ j ] − ( i − j ) ⋅ f ( p r e [ i ] ) } \displaystyle dp[i]=\max_{0\leq j<i}\left\{dp[j]-(i-j)\cdot f(pre[i])\right\} dp[i]=0j<imax{dp[j](ij)f(pre[i])}.

只需预处理 1 e 9 \sqrt{1\mathrm{e}9} 1e9 内的素数.

代码I

const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN];
umap<int, bool> bad;  // 坏素数
int primes[MAXN], cnt;
bool state[MAXN];
ll dp[MAXN];  // dp[i]表示操作完前i个数后增加的分数的最大值

void init() {
  for (int i = 2; i < MAXN; i++) {
    if (!state[i]) primes[cnt++] = i;

    for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
      state[primes[j] * i] = true;
      if (i % primes[j] == 0) break;
    }
  }
}

int f(int x) {
  int res = 0;
  for (int i = 0; i < cnt && (ll)primes[i] * primes[i] <= x; i++) {
    if (x % primes[i] == 0) {
      int cnt = 0;
      while (x % primes[i] == 0) x /= primes[i], cnt++;
      res += (bad[primes[i]] ? -1 : 1) * cnt;
    }
  }

  if (x > 1) res += bad[x] ? -1 : 1;
  return res;
}

void solve() {
  init();

  cin >> n >> m;
  for (int i = 1; i <= n; i++) cin >> a[i];
  while (m--) {
    int b; cin >> b;
    bad[b] = true;
  }

  ll ans = 0;
  for (int i = 1; i <= n; i++) ans += f(a[i]);  // 初始权值

  ll add = 0;  // 最大增加分数
  int g = 0;
  for (int i = 1; i <= n; i++) {
    g = gcd(g, a[i]);
    int tmp = -f(g);
    dp[i] = (ll)i * tmp;

    for (int j = 0; j < i; j++)
      dp[i] = max(dp[i], dp[j] + (ll)(i - j) * tmp);
    add = max(add, dp[i]);
  }
  cout << ans + add;
}

int main() {
  solve();
}

思路II

从后往前贪心可以使答案更优的下标 r r r,做操作 r r r并更新答案.

[] 设最优解中进行操作的下标为 r 1 > ⋯ > r k r_1>\cdots>r_k r1>>rk,贪心进行操作的下标为 l 1 > ⋯ > l m l_1>\cdots>l_m l1>>lm.

r 1 ≤ l 1 r_1\leq l_1 r1l1,这是因为贪心策略保证 > l 1 >l_1 >l1的下标都不能使得答案更优.

r 1 ≥ l 1 r_1\geq l_1 r1l1,若不然,则 r 1 < l 1 r_1<l_1 r1<l1,由前缀 gcd ⁡ \gcd gcd不增知:进行操作 l 1 l_1 l1能使得答案更优,矛盾.

l 1 = r 1 l_1=r_1 l1=r1,由数归知其他也对应相等.

例如,区间 [ 1 , 5 ] [1,5] [1,5] gcd ⁡ = 12 \gcd=12 gcd=12,区间 [ 1 , 9 ] [1,9] [1,9] gcd ⁡ = 4 \gcd=4 gcd=4,则先在下标 9 9 9的位置除以 4 4 4,再在下标 5 5 5的位置除以 3 3 3即可.

代码II

const int MAXN = 1e5 + 5;
int n, m;
int a[MAXN];
int pre[MAXN];  // 前缀gcd
umap<int, bool> bad;  // 坏素数
int primes[MAXN], cnt;
bool state[MAXN];

void init() {
  for (int i = 2; i < MAXN; i++) {
    if (!state[i]) primes[cnt++] = i;

    for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
      state[primes[j] * i] = true;
      if (i % primes[j] == 0) break;
    }
  }
}

int f(int x) {
  int res = 0;
  for (int i = 0; i < cnt && (ll)primes[i] * primes[i] <= x; i++) {
    if (x % primes[i] == 0) {
      int cnt = 0;
      while (x % primes[i] == 0) x /= primes[i], cnt++;
      res += (bad[primes[i]] ? -1 : 1) * cnt;
    }
  }

  if (x > 1) res += bad[x] ? -1 : 1;
  return res;
}

void solve() {
  init();

  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    pre[i] = gcd(pre[i - 1], a[i]);
  }
  while (m--) {
    int b; cin >> b;
    bad[b] = true;
  }

  ll ans = 0;
  for (int i = 1; i <= n; i++) ans += f(a[i]);  // 初始权值

  ll add = 0;  // 最大增加分数
  int div = 1;
  for (int i = n; i >= 1; i--) {
    int tmp = f(pre[i] /= div);
    if (tmp < 0) ans -= (ll)i * tmp, div *= pre[i];
  }
  cout << ans + add;
}

int main() {
  solve();
}


439C. Devu and Partitioning of the Array

原题指路:https://codeforces.com/problemset/problem/439/C

题意

给定 n n n个相异的整数 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an,将其划分为 k k k个不相交的非空集合,其中 p p p个集合的元素之和为偶数,另外 ( k − p ) (k-p) (kp)个集合的元素之和为奇数.若不存在划分方案,则输出"NO";否则第一行输出"YES",之后 k k k行每行输出一个集合的元素.

第一行输入三个整数 n , k , p    ( 1 ≤ k ≤ n ≤ 1 e 5 , 0 ≤ p ≤ k ) n,k,p\ \ (1\leq k\leq n\leq 1\mathrm{e}5,0\leq p\leq k) n,k,p  (1kn1e5,0pk).第二行输入 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).

思路

有解的条件:

①至少有 ( k − p ) (k-p) (kp)个奇数,组成 ( k − p ) (k-p) (kp)个由一个奇数组成的和为奇数的集合,

②奇数的个数与 ( k − p ) (k-p) (kp)的奇偶性相同.

​ 若不同,如奇数的个数 = k − p + 1 =k-p+1 =kp+1时,多出来的一个奇数会破坏一个和为奇数或偶数的集合.

③偶数的个数与剩下的奇数的个数的一半之和 ≥ p \geq p p,即组成和为奇数的集合后,多余的奇数两个一组构成和为偶数的集合.

对有解的情况,先构造 ( k − p ) (k-p) (kp)个和为奇数的集合,多余的奇数两个一组构成和为偶数的集合.将所有偶数一个一组构成和为偶数的集合.先输出构造出来的前 ( k − 1 ) (k-1) (k1)个集合作为答案,将其余的元素和为一个集合输出.

代码

void print(vi& a) {
  cout << a.size() << ' ';
  for (auto ai : a) cout << a << ' ';
  cout << endl;
}

void solve() {
  int n, k, p; cin >> n >> k >> p;
  vi even, odd;
  while (n--) {
    int a; cin >> a;
    if (a & 1) odd.push_back(a);
    else even.push_back(a);
  }

  bool ok = true;
  vector<vi> ans;
  if (odd.size() < k - p) ok = false;
  else if ((int)odd.size() %2 != (k - p) % 2) ok = false;
  else if (even.size() + (odd.size() - k + p) / 2 < p) ok = false;
  else {
    for (int i = 0; i < k - p; i++) {  // 一个奇数组成一个和为奇数的集合
      vi tmp;
      tmp.push_back(odd.back()), odd.pop_back();
      ans.push_back(tmp);
    }

    while (odd.size() >= 2) {  // 两个奇数组成一个和为偶数的集合
      vi tmp;
      tmp.push_back(odd.back()), odd.pop_back();
      tmp.push_back(odd.back()), odd.pop_back();
      ans.push_back(tmp);
    }

    for (auto i : even) {  // 一个偶数组成一个和为偶数的集合
      vi tmp;
      tmp.push_back(i);
      ans.push_back(tmp);
    }
  }

  if (!ok) cout << "NO";
  else {
    cout << "YES" << endl;
    for (int i = 0; i < k - 1; i++) print(ans[i]);

    vi rest;
    for (int i = k - 1; i < ans.size(); i++)
      for (auto j : ans[i]) rest.push_back(j);
    print(rest);
  }
}

int main() {
  solve();
}


490C. Hacking Cypher

原题指路:https://codeforces.com/problemset/problem/490/C

题意

给定一个长度不超过 1 e 6 1\mathrm{e}6 1e6的整数 s s s和两个整数 a , b    ( 1 ≤ a , b ≤ 1 e 8 ) a,b\ \ (1\leq a,b\leq 1\mathrm{e}8) a,b  (1a,b1e8).将 s s s分为不相交的两部分,使得两部分都不包含前导零,且第一部分是 a a a的倍数,第二部分是 b b b的倍数.若无解,输出"NO";否则输出"YES"并输出任一组解.

思路

s s s的长度为 n n n.求 s s s的前缀 s [ 1 ⋯ i ] s[1\cdots i] s[1i] a a a的值 x i x_i xi s s s的后缀 s [ ( n − i + 1 ) ⋯ n ] s[(n-i+1)\cdots n] s[(ni+1)n] b b b的值 y i y_i yi,因 s s s不含前导零,故只需检查第二部分是否有前导零即可.

代码

const int MAXN = 1e6 + 5;
char s[MAXN];
int a, b;
int x[MAXN], y[MAXN];

void solve() {
  cin >> s + 1 >> a >> b;

  int n = strlen(s + 1);
  for (int i = 1; i <= n; i++) 
    x[i] = ((ll)x[i - 1] * 10 % a + s[i] - '0') % a;

  int base = 1;
  for (int i = n; i > 1; i--) {
    y[i] = ((ll)(s[i] - '0') * base % b + (ll)y[i + 1]) % b;
    base = (ll)base * 10 % b;

    if (s[i] == '0') continue;  // 第二部分包含前导零
    if (!x[i - 1] && !y[i]) {
      cout << "YES" << endl;
      for (int j = 1; j <= i - 1; j++) cout << s[j];
      cout << endl;
      for (int j = i; j <= n; j++) cout << s[j];
      return;
    }
  }
  cout << "NO";
}

int main() {
  solve();
}


490D. Chocolate

原题指路:https://codeforces.com/problemset/problem/490/D

题意

给定两块大小分别为 a 1 × b 1 a_1\times b_1 a1×b1 a 2 × b 2 a_2\times b_2 a2×b2的矩形.现有如下两种操作:取其中一个矩形,①若该矩形可二等分,则将其二等分,保留 1 2 \dfrac{1}{2} 21;②若该矩形可三等分,则将其三等分,保留 2 3 \dfrac{2}{3} 32.问若干次操作后能否使得两矩形面积相同,若能,输出最小操作次数,并分别输出两矩形的边长;否则输出 − 1 -1 1.

思路

操作①等价于减少一个素因子 2 2 2;操作②等价于减少一个素因子 3 3 3,增加一个素因子 2 2 2.

求出各边长间相差的素因子 2 2 2 3 3 3的个数,做相应数目的操作后,检查最后得到的矩形的边长之积是否相等即可.

代码

int get(int x, int y) {  // 求x中包含因子y的个数
  int res = 0;
  while (x % y == 0) res++, x /= y;
  return res;
}

void solve() {
  int a1, b1, a2, b2; cin >> a1 >> b1 >> a2 >> b2;

  int two1 = get(a1, 2) + get(b1, 2), three1 = get(a1, 3) + get(b1, 3);
  int two2 = get(a2, 2) + get(b2, 2), three2 = get(a2, 3) + get(b2, 3);
  int dthree1 = max(three1 - three2, 0), dthree2 = max(three2 - three1, 0);
  int dtwo1 = max(two1 + dthree1 - two2 - dthree2, 0),
  		dtwo2 = max(two2 + dthree2 - two1 - dthree1, 0);

  int ans = dthree1 + dthree2 + dtwo1 + dtwo2;
  for (int i = 0; i < dthree1; i++) {
    if (a1 % 3 == 0) a1 = a1 / 3 * 2;
    else b1 = b1 / 3 * 2;
  }
  for (int i = 0; i < dthree2; i++) {
    if (a2 % 3 == 0) a2 = a2 / 3 * 2;
    else b2 = b2 / 3 * 2;
  }
  for (int i = 0; i < dtwo1; i++) {
    if (a1 % 2 == 0) a1 /= 2;
    else b1 /= 2;
  }
  for (int i = 0; i < dtwo2; i++) {
    if (a2 % 2 == 0) a2 /= 2;
    else b2 /= 2;
  }

  if ((ll)a1 * b1 != (ll)a2 * b2) {
    cout << -1;
    return;
  }

  cout << ans << endl;
  cout << a1 << ' ' << b1 << endl;
  cout << a2 << ' ' << b2 << endl;
}

int main() {
  solve();
}


546D. Soldier and Number Game

原题指路:https://codeforces.com/problemset/problem/546/D

题意 ( 3   s ) (3\ \mathrm{s}) (3 s)

给定两正整数 a , b    ( a ≥ b ) a,b\ \ (a\geq b) a,b  (ab),令 n = a ! b ! n=\dfrac{a!}{b!} n=b!a!.现有操作:选择一个 > 1 >1 >1的整数 x   s . t .   x ∣ n x\ s.t.\ x\mid n x s.t. xn,令 n / = x n/=x n/=x.求最大操作次数.

t    ( 1 ≤ t ≤ 1 e 6 ) t\ \ (1\leq t\leq 1\mathrm{e}6) t  (1t1e6)组测试数据.每组测试数据给定两个整数 a , b    ( 1 ≤ b ≤ a ≤ 5 e 6 ) a,b\ \ (1\leq b\leq a\leq 5\mathrm{e}6) a,b  (1ba5e6).

思路

显然每次操作取当前 n n n的素因子即可.

预处理出 5 e 6 5\mathrm{e}6 5e6以内每个数的素因子的个数 f a c t o r s [ ] factors[] factors[],求其前缀和 p r e [ ] pre[] pre[],则每个询问 a n s = p r e [ a ] − p r e [ b ] ans=pre[a]-pre[b] ans=pre[a]pre[b].

代码

const int MAXN = 5e6 + 5;
int primes[MAXN], cnt;
bool state[MAXN];
int factors[MAXN];  // factors[n]表示n的素因子的个数
ll pre[MAXN];  // factors[]的前缀和

void init() {
  for (int i = 2; i < MAXN; i++) {
    if (!state[i]) primes[cnt++] = i, factors[i] = 1;

    for (int j = 0; (ll)primes[j] * i < MAXN; j++) {
      state[primes[j] * i] = true;
      factors[primes[j] * i] = factors[i] + 1;
      if (i % primes[j] == 0) break;
    }
  }

  for (int i = 1; i < MAXN; i++) pre[i] = pre[i - 1] + factors[i];
}

void solve() {
  int a, b; cin >> a >> b;
  cout << pre[a] - pre[b] << endl;
}

int main() {
  init();
  CaseT  // 单测时注释掉该行
    solve();
}


552C. Vanya and Scales

原题指路:https://codeforces.com/problemset/problem/552/C

题意

给定两个整数 w , m    ( 2 ≤ w ≤ 1 e 9 , 1 ≤ m ≤ 1 e 9 ) w,m\ \ (2\leq w\leq 1\mathrm{e}9,1\leq m\leq 1\mathrm{e}9) w,m  (2w1e9,1m1e9).问 m m m能否由 w 0 , w 1 , ⋯   , w 100 w^0,w^1,\cdots,w^{100} w0,w1,,w100线性表出.

思路

暴力模拟 m m m w w w表示的过程即可,时间复杂度 O ( log ⁡ m ) O(\log m) O(logm).

注意判断 w 0 w^0 w0时要先判断 ( m − 1 ) (m-1) (m1)能否被 w w w整除,再判断 ( m + 1 ) (m+1) (m+1)能否被 w w w整除,否则 m = 1 m=1 m=1时死循环.

代码

void solve() {
  int w, m; cin >> w >> m;

  while (m) {
    if (m % w) {  // 不整除才要判
      if ((m - 1) % w == 0) m--;  // 注意先判m-1
      else if ((m + 1) % w == 0) m++;
      else {
        cout << "NO";
        return;
      }
    }

    m /= w;
  }
  cout << "YES";
}

int main() {
  solve();
}


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[Codeforces] number theory (R1900) Part.2 的相关文章

随机推荐

  • 面试题——1

    3 无重复字符的最长子串 206 反转链表 215 数组中的第K个最大元素
  • ValueError: Input 0 of layer sequential is incompatible with the layer: : expected min_ndim=4, found

    最近在做猫狗二分类实验的时候 在网上找到了教程 然后跟着教程打代码发现最后出现了ValueError Input 0 of layer sequential is incompatible with the layer expected m
  • Java访问权限修饰符

    private 私有的 只有在同一个类才能访问 内部类 成员变量 方法 default friendly 默认的 同一个包中可以访问 其他包中不能访问 类 成员变量 方法 接口 protected 受保护的 同一个包中可以访问 不同的包需要
  • KubeSphere 部署安装

    使用 kubeadm 搭建的 Kubernetes 1 15 2 版本集群 Helm v2 12 2 版本 使用 NFS 作为集群存储后端 下载安装脚本 mkdir root kubeSphere cd root kubeSphere gi
  • Git仓库完全迁移,包括所有的分支和标签,当然也包括日志。

    度娘了一堆git仓库迁移的内容 一个个都比较麻烦 而且本地下了代码 还要删去库地址 再切换到新库的地址上传 一般这种操作都只是master分支 其他分支还要一个一个来 后来在51CTO上找了一个文章 简单明了 一下就全搞定了 包括所有的分支
  • 二进制安装Docker

    下载 安装 wget https download docker com linux static stable x86 64 docker 19 03 6 tgz tar zvxf docker 19 03 6 tgz 把文件copy到
  • 计算机常用控温算法,常用温度控制方法原理 -解决方案-华强电子网

    常用PID调节器 温控仪控制算法包括常规PID 模糊控制 神经网络 Fuzzy PID 神经网络PID 模糊神经网络 遗传PID及广义预测等算法 常规PID控制易于建立线性温度控制系统被控对象模型 模糊控制基于规则库 并以绝对或增量形式给出
  • random函数汇总

    1 random random random random 用于生成一个0到1之间的随机浮点数 0 lt n lt 1 gt gt gt random random 0 7086588033796296 2 random uniform r
  • 【Django知识补充 - 1】:admin站点和rest_framework实现文件的上传和下载

    文章目录 项目准备 settings py中的配置 主路由urls py的配置 子应用中的文件代码 init py admin py models py serializers py urls py views py 演示 在admin中上
  • java实现-合并两个有序数组

    合并两个有序数组 给你两个有序整数数组 nums1 和 nums2 请你将 nums2 合并到 nums1 中 使 nums1 成为一个有序数组 初始化 nums1 和 nums2 的元素数量分别为 m 和 n 你可以假设 nums1 的空
  • 移动端 文件预览(所有文档文件类型)

  • Java21天打卡-Day15 数组

    import java util Arrays public class Day15 数组 题目1 创建一个长度是8的字符串数组 使用8个长度是5的随机字符串初始化这个数组 对这个数组进行排序 按照每个字符串的首字母排序 无视大小写 注1
  • PermissionX 1.7发布,全面支持Android 13运行时权限

    各位小伙伴们大家早上好 一年一度的PermissionX升级又来了 还记得上次发布PermissionX 1 6版本还是在去年10月份的时候 当时是对Android 12系统进行了支持 详情可以参考这篇文章 PermissionX 1 6发
  • 深度学习框架Pytorch傻瓜式安装教程

    前提 已经安装完minianaconda和pycharm minianaconda直接官网下载即可 minianaconda比起anaconda体量很小 pycharm专业版2020下载百度网盘链接 链接 https pan baidu c
  • 【Detectron2】入门02-使用自己的数据集

    Detectron2 official Documents https detectron2 readthedocs io tutorials datasets html 目录 COCO格式数据集 Standard dataset dict
  • Qt对象树

    对象树 Qt提供了对象树机制 能够自动 有效的组织和管理继承自QObject的Qt对象 每个继承自QObject类的对象通过它的对象链表 QObjectList 来管理子类对象 当用户创建一个子对象时 其对象链表相应更新子类对象信息 对象链
  • JVM优化

    java运行时数据区 程序计数器 线程私有 java虚拟机栈 线程私有 本地方法栈 线程私有 java堆 线程公用 方法区 线程公用 jvm内存分配 栈内存分配 私有的 不会存在线程安全 保存参数 局部变量 中间计算过程和其他的数据 退出方
  • redis复习

    1 关系型数据库和非关系型数据库 关系型数据库 Relational Database 和非关系型数据库 Non relational Database 或 NoSQL Database 之间的主要区别可以从以下几个方面进行理解 1 1 数
  • 机器学习(数据分析)数学基础——线性代数篇(五)线性方程组

    求解线性方程组也算是考研中的必备技能了 它往往出现在大题的第一问 注 本篇需要一些线性代数基础 1 首先我们来解决r n的情况 线性方程组 import numpy as np from scipy import linalg 定义A矩阵
  • [Codeforces] number theory (R1900) Part.2

    Codeforces number theory R1900 Part 2 题单 https codeforces com problemset page 1 tags number 20theory 1601 1900 294C Shaa