[Codeforces] number theory (R1900) Part.1

[Codeforces] number theory (R1900) Part.1

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

7C. Line

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

题意

给定三个整数 A , B , C    ( − 2 e 9 ≤ A , B , C ≤ 2 e 9 , A 2 + B 2 > 0 ) A,B,C\ \ (-2\mathrm{e}9\leq A,B,C\leq 2\mathrm{e}9,A^2+B^2>0) A,B,C  (2e9A,B,C2e9,A2+B2>0).在直线 A x + B y + C Ax+By+C Ax+By+C上求一个整点 ( x , y )   s . t .   x , y ∈ [ − 5 e 18 , 5 e 18 ] (x,y)\ s.t.\ x,y\in [-5\mathrm{e}18,5\mathrm{e}18] (x,y) s.t. x,y[5e18,5e18],若存在则输出任一满足条件的整点;否则输出 − 1 -1 1.

思路

即求不定方程 A x + B y = − C Ax+By=-C Ax+By=C的一组在 [ − 5 e 18 , 5 e 18 ] [-5\mathrm{e}18,5\mathrm{e}18] [5e18,5e18]范围内的整数解,exgcd求即可.

在这里插入图片描述

exgcd求出的解必在要求的范围内.

[] 如上图,显然无解的情况只能是线段 A B AB AB上只有 A A A B B B两个整点,且整点都落在要求的范围之外.

设点 A A A B B B的横坐标、纵坐标之差分别为 Δ x \Delta x Δx Δ y \Delta y Δy,则线段 A B AB AB上的整点数为 gcd ⁡ ( Δ x , Δ y ) + 1 \gcd(\Delta x,\Delta y)+1 gcd(Δx,Δy)+1.

为使得要求的范围内无整点,需 gcd ⁡ ( Δ x , Δ y ) = 1 \gcd(\Delta x,\Delta y)=1 gcd(Δx,Δy)=1.

​ ① Δ x = 1 \Delta x=1 Δx=1时, Δ y > 1 e 19 \Delta y> 1\mathrm{e}19 Δy>1e19,则斜率 ∣ k ∣ > 1 e 19 |k|>1\mathrm{e}19 k>1e19.

​ ② Δ y = 1 \Delta y=1 Δy=1时, Δ x > 1 e 19 \Delta x>1\mathrm{e}19 Δx>1e19,则斜率 ∣ k ∣ < 1 1 e 19 |k|<\dfrac{1}{1\mathrm{e}19} k<1e191.

对题目中的直线 A x + B y + C = 0 Ax+By+C=0 Ax+By+C=0,

​ ①若 A = 0 A=0 A=0 B = 0 B=0 B=0,则直线是垂直于坐标轴轴的直线,显然此时要么无整点,要么有很多在范围内的整点.

​ ②若 B ≠ 0 B\neq 0 B=0,则直线斜率的绝对值 ∣ k ∣ = ∣ B A ∣ ≤ 2 e 9 1 = 2 e 9 < 1 e 19 , ∣ k ∣ ≥ 1 4 e 9 > 1 e 19 |k|=\left|\dfrac{B}{A}\right|\leq \dfrac{2\mathrm{e}9}{1}=2\mathrm{e}9<1\mathrm{e}19,|k|\geq\dfrac{1}{4\mathrm{e}9}>\dfrac{1}{\mathrm{e}19} k= AB 12e9=2e9<1e19,k4e91>e191.

综上,若有解,则exgcd求出的解必在要求的范围内.

代码

ll exgcd(ll a, ll b, ll& x, ll& y) {  // 扩欧
  if (!b) {
    x = 1, y = 0;
    return a;
  }

  ll d = exgcd(b, a % b, y, x);
  y -= a / b * x;
  return d;
}

void solve() {
  ll a, b, c; cin >> a >> b >> c;

  ll x, y;
  ll d = exgcd(a, b, x, y);
  if (c % d) cout << -1;
  else {
    c *= -1;
    x *= c / d, y *= c / d;
    cout << x << ' ' << y;
  }
}

int main() {
  solve();
}


16C. Monitor

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

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

给定一个长为 a a a、宽为 b b b的矩形,缩小其长或宽直至长宽比为 x : y x:y x:y.若有解,则输出缩小后面积最大的矩形对应的长和宽,否则输出"0 0".

第一行输入四个整数 a , b , x , y    ( 1 ≤ a , b , x , y ≤ 2 e 9 ) a,b,x,y\ \ (1\leq a,b,x,y\leq 2\mathrm{e}9) a,b,x,y  (1a,b,x,y2e9).

思路

显然将 x y \dfrac{x}{y} yx约分不影响结果,下面讨论中已将其约分.

若初始时 a < x a<x a<x b < y b<y b<y,显然无解;否则将 x y \dfrac{x}{y} yx放大 k = min ⁡ { a x , b y } k=\min\left\{\dfrac{a}{x},\dfrac{b}{y}\right\} k=min{xa,yb}倍即得最大面积.

代码

void solve() {
  int a, b, x, y; cin >> a >> b >> x >> y;

  int d = gcd(x, y);
  x /= d, y /= d;

  if (a < x || b < y) {
    cout << "0 0";
    return;
  }

  int k = min(a / x, b / y);
  cout << x * k << ' ' << k * y;
}

int main() {
  solve();
}


66D. Petya and His Friends

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

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

构造一个长度为 n    ( 2 ≤ n ≤ 50 ) n\ \ (2\leq n\leq 50) n  (2n50)的正整数序列,满足如下三个要求:①对 ∀ i , j ∈ [ 1 , n ] \forall i,j\in [1,n] i,j[1,n],由 gcd ⁡ ( a i , a j ) ≠ 1 \gcd(a_i,a_j)\neq 1 gcd(ai,aj)=1;②所有元素的 gcd ⁡ = 1 \gcd=1 gcd=1;③所有元素两两相异.若有解,输出任一组解;否则输出 − 1 -1 1.

思路I

(1) n = 2 n=2 n=2时,显然无解.

(2) n ≥ 3 n\geq 3 n3时,取前三个数为 a b , a c , b c ab,ac,bc ab,ac,bc,如 6 , 15 , 10 6,15,10 6,15,10,则它们的 gcd ⁡ = 1 \gcd=1 gcd=1,剩下的数只需保证不与前三个数互素,显然可取 10 10 10的倍数.

代码I

void solve() {
  int n; cin >> n;

  if (n == 2) {
    cout << -1;
    return;
  }

  cout << 6 << endl;
  cout << 15 << endl;
  for (int i = 1; i <= n - 2; i++) cout << 10 * i << endl;
}

int main() {
  solve();
}

思路II

预处理出前 50 50 50个素数 p 1 , ⋯   , p 50 p_1,\cdots,p_{50} p1,,p50,令 A = ∏ i = 1 50 p i \displaystyle A=\prod_{i=1}^{50} p_i A=i=150pi,取 A p 1 , ⋯   , A p 50 \dfrac{A}{p_1},\cdots,\dfrac{A}{p_{50}} p1A,,p50A即可.本做法需要高精度.



111B. Petya and Divisors

原题指路:https://codeforces.com/problemset/problem/111/B

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

给定 n n n个询问 x i   y i x_i\ y_i xi yi,对每个询问,求 x i x_i xi有多少个约数不整除 x i − y i , x i − y i + 1 , ⋯   , x i − 1 x_{i-y_i},x_{i-y_i+1},\cdots,x_{i-1} xiyi,xiyi+1,,xi1,初始时 y i = 0 y_i=0 yi=0.

第一行输入一个整数 n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5).接下来 n n n行每行输入两个整数 x i , y i    ( 1 ≤ x i ≤ 1 e 5 , 0 ≤ y i ≤ i − 1 , 1 ≤ i ≤ n ) x_i,y_i\ \ (1\leq x_i\leq 1\mathrm{e}5,0\leq y_i\leq i-1,1\leq i\leq n) xi,yi  (1xi1e5,0yii1,1in).

思路

用map记录每个约数最后出现的位置即可.

代码

umap<int, int> mp;  // 记录每个约数最后出现的位置

bool insert(int pos, int lim, int d) {  // 记录约数最后出现的位置,返回是否合法
  if (mp[d] >= pos - lim) {  // 约数在范围内出现过
    mp[d] = pos;  // 更新约数最后出现的位置
    return false;
  }

  mp[d] = pos;  // 更新约数最后出现的位置
  return true;
}

void solve() {
  int n; cin >> n;
  for (int i = 1; i <= n; i++) {
    int x, y; cin >> x >> y;

    int ans = 0;
    for (int d = 1; (ll)d * d <= x; d++) {
      if (x % d == 0) {
        ans += insert(i, y, d);
        if (d != x / d) ans += insert(i, y, x / d);
      }
    }
    cout << ans << endl;
  }
}

int main() {
  solve();
}


117B. Very Interesting Game

原题指路:https://codeforces.com/problemset/problem/117/B

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

给定三个整数 a , b , c    ( 0 ≤ a , b ≤ 1 e 9 , 1 ≤ c ≤ 1 e 7 ) a,b,c\ \ (0\leq a,b\leq 1\mathrm{e}9,1\leq c\leq 1\mathrm{e}7) a,b,c  (0a,b1e9,1c1e7).A写一个 9 9 9位的十进制数 s 1 s_1 s1(允许有前导零,下同)使得它不超过 a a a;B写一个 9 9 9位的十进制数 s 2 s_2 s2使得它不超过 b b b.若 s 1 s_1 s1 s 2 s_2 s2拼接而成的数能被 c c c整除则B胜;否则A胜.两人都采取最优策略,若B胜,输出 2 2 2;否则先输出 1 1 1,再输出A可以选择的字典序最小的 s 1 s_1 s1.

思路

设A选择数 x ≤ a x\leq a xa,考察 r = 1 0 9 ⋅ x   m o d   c r=10^9\cdot x\ \mathrm{mod}\ c r=109x mod c.遍历所有 x x x,若 c − r ≤ b c-r\leq b crb,则B必胜.显然至多需遍历 c c c次.

代码

const int BASE = 1e9;

void solve() {
  int a, b, c; cin >> a >> b >> c;

  for (int x = 0; x <= min(a, c); x++) {  // 枚举A选择的数
    int r = ((-(ll)BASE * x) % c + c) % c;
    if (0 <= r && r <= b) continue;

    printf("1 %09d", x);
    return;
  }
  printf("2");
}

int main() {
  solve();
}


121C. Lucky Permutation

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

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

只包含数码 4 4 4 7 7 7的数字是好的.对 1 ∼ n    ( 1 ≤ n ≤ 1 e 9 ) 1\sim n\ \ (1\leq n\leq 1\mathrm{e}9) 1n  (1n1e9)的所有全排列,求字典序第 k    ( 1 ≤ k ≤ 1 e 9 ) k\ \ (1\leq k\leq 1\mathrm{e}9) k  (1k1e9)小的全排列中有多少个好的数字的下标也是好的,排列的下标从 1 1 1开始.若不存在第 k k k小的全排列,输出 − 1 -1 1.

思路

不存在字典序第 k k k小的全排列当且仅当 k > n ! k>n! k>n!,而 13 ! > 1 e 9 13!>1\mathrm{e}9 13!>1e9,故可暴力检查 k k k是否 ≤ n ! \leq n! n!,若否则无解. n ≥ 13 n\geq 13 n13时必有解.

预处理出 1 e 9 1\mathrm{e}9 1e9内的好的数字.因 13 ! > 1 e 9 13!>1\mathrm{e}9 13!>1e9,则字典序前 k k k小的全排列中至多后 13 13 13位改变,且这后 13 13 13位是 1 ∼ n 1\sim n 1n中最大的 13 13 13个数,用逆Cantor展开还原序列即可.

代码

const int MAXN = 15;
int fac[MAXN];
vi res;  // 将1~n中最大的lim个数加入
int ans;

void dfs(ll cur, int lim) {  // 预处理[1,lim]范围内好的数的个数
  if (cur > lim) return;

  ans += cur != 0;  // 去掉初始的0
  dfs(cur * 10 + 4, lim), dfs(cur * 10 + 7, lim);
}

int find(int k) {  // 求剩下的元素中的第(k+1)小的元素
  sort(all(res));
  int tmp = res[k];
  res.erase(res.begin() + k);
  return tmp;
}

bool check(int n) {  // 判断一个数是否是好的
  string s = to_string(n);
  for (auto ch : s)
    if (ch != '4' && ch != '7') return false;
  return true;
}

void solve() {
  int n, k; cin >> n >> k;
  k--;  // 逆Cantor展开第一步的-1

  int lim = -1;  // >k的最小的阶乘
  fac[0] = 1;
  for (int i = 1; i <= n; i++) {
    fac[i] = i * fac[i - 1];
    res.push_back(n - i + 1);  // 将1~n中最大的lim个数加入集合

    if (fac[i] > k) {
      lim = i;
      break;
    }
  }

  if (lim == -1) {  // k>n!
    cout << -1;
    return;
  }

  dfs(0, n - lim);

  for (int i = lim; i >= 1; i--) {  // 逆Cantor展开还原后lim个数
    int x = find(k / fac[i - 1]), pos = n - i + 1;  // 全排列中倒数第i个数及其下标
    if (check(x) && check(pos)) ans++;
    k %= fac[i - 1];
  }
  cout << ans;
}

int main() {
  solve();
}


134B. Pairs of Numbers

原题指路:https://codeforces.com/problemset/problem/134/B

题意

初始时给定一个数对 ( a , b ) (a,b) (a,b),它可产生两个数对 ( a + b , b ) (a+b,b) (a+b,b) ( a , a + b ) (a,a+b) (a,a+b).给定一个整数 n    ( 1 ≤ n ≤ 1 e 6 ) n\ \ (1\leq n\leq 1\mathrm{e}6) n  (1n1e6),问从 ( 1 , 1 ) (1,1) (1,1)开始至少需要多少步可使得数对中出现至少一个 n n n.

思路

首先一定有解,至少可以做 n n n + 1 +1 +1.

考虑DFS,显然直接DFS会TLE.考虑从结果开始DFS,当 a = b = 1 a=b=1 a=b=1时更新答案.

代码I

int ans = INF;

void dfs(int a, int b, int step) {  // 当前数对为(a,b),步数为step
  if (a < 1 || b < 1 || step >= ans) return;  // 非法解或已不是最优解
  if (a == 1 && b == 1) ans = min(ans, step);  // 找到合法解

  if (a - b > 0) dfs(a - b, b, step + 1);
  if (b - a > 0) dfs(a, b - a, step + 1);
}

void solve() {
  int n; cin >> n;

  for (int i = 1; i <= n; i++) dfs(i, n, 0);
  cout << ans;
}

int main() {
  solve();
}

代码II

int ans = INF;

int dfs(int a, int b) {  // 当前数对为(a,b)
  if (a == 1 && b == 1) return 0;

  if (a > b) swap(a, b);  // 保证a≤b
  if (a < 1 || b < 1 || a == b) return INF;  // 非法解
  return (b - 1) / a + dfs((b - 1) % a + 1, a);
}

void solve() {
  int n; cin >> n;

  for (int i = 1; i <= n; i++) ans = min(ans, dfs(i, n));
  cout << ans;
}

int main() {
  solve();
}


222C. Reducing Fractions

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

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

给定两个集合 S 1 , S 2 S_1,S_2 S1,S2,其中 S 1 S_1 S1包含 n    ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n  (1n1e5)个元素 a 1 , ⋯   , a n    ( 1 ≤ a i ≤ 1 e 7 ) a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}7) a1,,an  (1ai1e7), S 2 S_2 S2包含 m    ( 1 ≤ m ≤ 1 e 5 ) m\ \ (1\leq m\leq 1\mathrm{e}5) m  (1m1e5)个元素 b 1 , ⋯   , b m    ( 1 ≤ b i ≤ 1 e 7 ) b_1,\cdots,b_m\ \ (1\leq b_i\leq 1\mathrm{e}7) b1,,bm  (1bi1e7). S 1 S_1 S1中的元素之积为分数的分子, S 2 S_2 S2中的元素之积为分母.求一个两个分别于 S 1 , S 2 S_1,S_2 S1,S2等价的集合 T 1 , T 2 T_1,T_2 T1,T2,使得 T 1 T_1 T1中的元素之积为分子, T 2 T_2 T2中的元素之积为分母,且得到的结果为既约分数.

思路

模拟约分过程即可.

注意分别对每个数做素因数分解,时间复杂度 O ( max ⁡ { n , m } max ⁡ { a , b } ) O\left(\max\{n,m\}\sqrt{\max\{a,b\}}\right) O(max{n,m}max{a,b} ),会TLE.

可用线性筛或埃筛预处理出每个数的最小素因子,素因数分解时每次除以其最小素因子,时间复杂度 O ( max ⁡ { n , m } log ⁡ max ⁡ { a , b } ) O\left(\max\{n,m\}\log{\max\{a,b\}}\right) O(max{n,m}logmax{a,b}).

代码

const int MAXN = 1e7 + 5;
int n, m;
int primes[MAXN];  // 记录每个数的最小素因子
int up[MAXN], down[MAXN];  // 分子、分母中每个素因子的次数

void init() {  // 预处理出每个数的最小素因子
  for (int i = 2; i < MAXN; i++) {
    if (!primes[i]) {
      primes[i] = i;

      for (int j = 2 * i; j < MAXN; j += i) primes[j] = i;
    }
  }
}

void solve() {
  init();

  cin >> n >> m;
  vi a(n);
  for (auto& ai : a) {
    cin >> ai;
    for (int j = ai; j > 1; j /= primes[j]) up[primes[j]]++;  // 素因数分解
  }
  vi b(m);
  for (auto& bi : b) {
    cin >> bi;
    for (int j = bi; j > 1; j /= primes[j]) down[primes[j]]++;  // 素因数分解
  }

  cout << n << ' ' << m << endl;
  for (auto ai : a) {
    int res = 1;
    for (int j = ai; j > 1; j /= primes[j]) {
      if (down[primes[j]]) down[primes[j]]--;  // 可以约分
      else res *= primes[j];  // 不能约分
    }
    cout << res << ' ';
  }
  cout << endl;
  for (auto bi : b) {
    int res = 1;
    for (int j = bi; j > 1; j /= primes[j]) {
      if (up[primes[j]]) up[primes[j]]--;  // 可以约分
      else res *= primes[j];  // 不能约分
    }
    cout << res << ' ';
  }
}

int main() {
  solve();
}


223C. Partial Sums

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

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

给定一个长度为 n n n的序列 a 1 , ⋯   , a n a_1,\cdots,a_n a1,,an.现有操作,分为如下三步:①求 a [ ] a[] a[]的前缀和数组 p r e [ ] pre[] pre[],即 p r e [ 1 ] = a [ 1 ] , p r e [ i ] = p r e [ i − 1 ] + a [ i ]    ( i ≥ 2 ) pre[1]=a[1],pre[i]=pre[i-1]+a[i]\ \ (i\geq 2) pre[1]=a[1],pre[i]=pre[i1]+a[i]  (i2);②将 p r e [ ] pre[] pre[]的元素都对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模;③将 p r e [ ] pre[] pre[]作为新的 a [ ] a[] a[].问 k k k次操作后得到的序列 a [ ] a[] a[].

第一行输入两个整数 n , k    ( 1 ≤ n ≤ 2000 , 0 ≤ k ≤ 1 e 9 ) n,k\ \ (1\leq n\leq 2000,0\leq k\leq 1\mathrm{e}9) n,k  (1n2000,0k1e9).第二行输入 n n n各整数 a 1 , ⋯   , a n    ( 0 ≤ a i ≤ 1 e 9 ) a_1,\cdots,a_n\ \ (0\leq a_i\leq 1\mathrm{e}9) a1,,an  (0ai1e9).

思路I

B i j = [ i ≥ j ] B_{ij}=[i\geq j] Bij=[ij],则 p r e i = ∑ j = 1 n B i j a j \displaystyle pre_i=\sum_{j=1}^n B_{ij}a_j prei=j=1nBijaj.将 a [ ] a[] a[] p r e [ ] pre[] pre[]视为列向量,则每次操作等价于 a → \overrightarrow a a 左乘转移矩阵 B = ( B i j ) n × n B=(B_{ij})_{n\times n} B=(Bij)n×n.用矩阵快速幂求,时间复杂度 O ( n 3 log ⁡ k ) O(n^3\log k) O(n3logk),会TLE.

考虑优化,注意到 ( B k ) i , j = ( B k ) i − j + 1 , 1 (B^k)_{i,j}=(B^k)_{i-j+1,1} (Bk)i,j=(Bk)ij+1,1,即矩阵 B k B^k Bk中与主对角线平行的对角线上的元素都相等,这可用数学归纳法证明.这表明:用序列 ( b k ) i = ( B k ) i , 1 (b^k)_i=(B^k)_{i,1} (bk)i=(Bk)i,1即可确定矩阵 B k B^k Bk,即矩阵 B k B^k Bk第一列的元素.矩阵 B k B l B^k B^l BkBl的第一列的元素 ( b k + l ) = ∑ j = 1 i ( b k ) j ( b l ) i − j + 1 \displaystyle (b^{k+l})=\sum_{j=1}^i (b^k)_j (b^l)_{i-j+1} (bk+l)=j=1i(bk)j(bl)ij+1.用矩阵快速幂求,时间复杂度 O ( n 2 log ⁡ k ) O(n^2\log k) O(n2logk),可过.

事实上本题还可进一步优化.下面用数学归纳法证明 ( b k ) i = C k + i − 1 k − 1 (b^k)_i=C_{k+i-1}^{k-1} (bk)i=Ck+i1k1.

[] ( b k + 1 ) i = ∑ j = 1 k C k + j − 1 k − 1 = C k k + C k k − 1 + ∑ j = 3 i C k + j − 1 k − 1 = C k k + 1 + C k + 1 k − 1 + ∑ j = 4 i C k + j − 1 k = ⋯ = C k + i k \displaystyle (b^{k+1})_i=\sum_{j=1}^k C_{k+j-1}^{k-1}=C_k^k + C_k^{k-1}+\sum_{j=3}^i C_{k+j-1}^{k-1}=C_k^{k+1}+C_{k+1}^{k-1}+\sum_{j=4}^i C_{k+j-1}^k=\cdots=C_{k+i}^k (bk+1)i=j=1kCk+j1k1=Ckk+Ckk1+j=3iCk+j1k1=Ckk+1+Ck+1k1+j=4iCk+j1k==Ck+ik.

注意到 ( b k ) i + 1 ( b k ) i = ( k + i ) ( i + 1 ) \dfrac{(b^k)_{i+1}}{(b^k)_i}=\dfrac{(k+i)}{(i+1)} (bk)i(bk)i+1=(i+1)(k+i),故 ( b k ) i = k + i − 1 i ( b k ) i − 1 (b^k)_i=\dfrac{k+i-1}{i}(b^k)_{i-1} (bk)i=ik+i1(bk)i1,递推求即可.

另一做法: p r e i k = ∑ j = 1 i C k + i − j i − j a j \displaystyle pre_i^k=\sum_{j=1}^i C_{k+i-j}^{i-j} a_j preik=j=1iCk+ijijaj.因 k k k的范围较大,无法预处理出所有组合数.注意到 i − j ≤ n ≤ 2000 i-j\leq n\leq 2000 ijn2000,可只处理出所有 ( i − j ) (i-j) (ij)可能的取值对应的组合数,总时间复杂度 O ( n 2 ) O(n^2) O(n2).

代码

const int MAXN = 2005;
const int MOD = 1e9 + 7;
int n, k;
int a[MAXN];
int inv[MAXN];
int C[MAXN];  // C[i-j]表示组合数C(k+i-j,i-j)

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

  C[0] = C[1] = 1;
  for (int i = 1; i <= n; C[++i] = 1)
    for (int j = 1; j <= i; j++) C[i] = (ll)C[i] * (k + j - 1) % MOD * inv[j] % MOD;
}

void solve() {
  cin >> n >> k;
  for (int i = 1; i <= n; i++) cin >> a[i];

  init();

  for (int i = 1, ans = 0; i <= n; i++, ans = 0) {
    for (int j = 1; j <= i; j++) ans = ((ll)ans + (ll)C[i - j] * a[j] % MOD) % MOD;
    cout << ans << ' ';
  }
}

int main() {
  solve();
}

思路II

[引理] 对形式幂级数 A ( x ) A(x) A(x) p ∈ R p\in\mathbb{R} pR,若 F ( x ) = [ A ( x ) ] p F(x)=[A(x)]^p F(x)=[A(x)]p,则 A ( x ) F ′ ( x ) = p F ( x ) A ′ ( x ) A(x)F'(x)=pF(x)A'(x) A(x)F(x)=pF(x)A(x).

[] ln ⁡ F ( x ) = p ln ⁡ A ( x ) \ln F(x)=p\ln A(x) lnF(x)=plnA(x),两边对 x x x求导,移项即证.

对序列 a 0 , ⋯   , a n − 1 a_0,\cdots,a_{n-1} a0,,an1,设其生成函数为 A ( x ) A(x) A(x),则求 { a n } \{a_n\} {an}的前缀和 { b n } \{b_n\} {bn}等价于给 A ( x ) A(x) A(x)乘上 1 1 − x \dfrac{1}{1-x} 1x1.

[] A ( x ) A(x) A(x)的常数项 a 0 a_0 a0求前缀和得到 b 0 = a 0 b_0=a_0 b0=a0,即 A ( x ) A(x) A(x)乘上 1 1 1.

A ( x ) A(x) A(x)的一次项 a 1 a_1 a1求前缀和得到 b 1 = a 0 + a 1 b_1=a_0+a_1 b1=a0+a1,即 A ( x ) A(x) A(x)乘上 x x x.

同理易得求前缀和的过程即 A ( x ) A(x) A(x)乘上 1 + x + x 2 + ⋯ = 1 1 − x    ( ∣ x ∣ < 1 ) 1+x+x^2+\cdots=\dfrac{1}{1-x}\ \ (|x|<1) 1+x+x2+=1x1  (x<1).

F ( x ) = ∑ n ∞ f n x n \displaystyle F(x)=\sum_n^\infty f_nx^n F(x)=nfnxn,则 F ( x ) = 1 ( 1 − x ) k F(x)=\dfrac{1}{(1-x)^k} F(x)=(1x)k1.

由引理: ( 1 − x ) F ′ ( x ) = − k F ( x ) ⋅ ( − 1 ) = k F ( x ) \displaystyle (1-x)F'(x)=-kF(x)\cdot (-1)=kF(x) (1x)F(x)=kF(x)(1)=kF(x),即 ∑ n = 0 ∞ ( n + 1 ) f n + 1 x n − ∑ n = 1 ∞ n f n x n = k ∑ n = 0 ∞ f n x n \displaystyle \sum_{n=0}^\infty (n+1)f_{n+1}x^n-\sum_{n=1}^\infty nf_n x^n=k\sum_{n=0}^\infty f_nx^n n=0(n+1)fn+1xnn=1nfnxn=kn=0fnxn.

{ f 0 = F ( 0 ) = 1 f 0 + 1 = F ( 0 + 1 ) = k f 0 ⇒ f 1 = k ( n + 1 ) f n + 1 − n f n = k f n    ( n ≥ 1 ) \begin{cases}f_0=F(0)=1 \\ f_{0+1}=F(0+1)=kf_0\Rightarrow f_1=k \\ (n+1) f_{n+1} -n f_n=k f_n\ \ (n\geq 1)\end{cases} f0=F(0)=1f0+1=F(0+1)=kf0f1=k(n+1)fn+1nfn=kfn  (n1),故 f n = n + k − 1 n f n − 1 f_n=\dfrac{n+k-1}{n}f_{n-1} fn=nn+k1fn1,由此可见 f n f_n fn是组合数.

O ( n ) O(n) O(n)递推出 { f n } \{f_n\} {fn} O ( n 2 ) O(n^2) O(n2)地求 A ( x ) A(x) A(x) F ( x ) F(x) F(x)的卷积即可.因 n ≤ 2000 n\leq 2000 n2000,暴力求即可.



279E. Beautiful Decomposition

原题指路:https://codeforces.com/problemset/problem/279/E

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

给定一个长度不超过 1 e 6 1\mathrm{e}6 1e6的不含前导零的二进制数 n n n,求至少要用多少个形如 2 k 2^k 2k − 2 k -2^k 2k的数使得它们之和为 n n n.

思路I

每个 2 2 2的幂次至多使用一次.

[] ①加两次 2 k 2^k 2k相当于加一次 2 k + 1 2^{k+1} 2k+1;②减两次 2 k 2^k 2k相当于减一次 2 k + 1 2^{k+1} 2k+1;③加一次并减一次 2 k 2^k 2k相当于无操作.

n n n的最高位的权值为 2 k 2^k 2k.注意到 2 0 + ⋯ + 2 k − 1 < 2 k 2^0+\cdots+2^{k-1}<2^k 20++2k1<2k,则为使得 s s s的最高位为 1 1 1,需加上某个 2 x    ( x ≥ k ) 2^x\ \ (x\geq k) 2x  (xk).注意到加上 x ≥ k + 2 x\geq k+2 xk+2不是最优的,如加上 2 k + 2 2^{k+2} 2k+2后还需减去一个 2 k + 1 2^{k+1} 2k+1,不如只加上一个 2 k + 1 2^{k+1} 2k+1.故 x = k x=k x=k k + 1 k+1 k+1.

①若选择加上 2 k 2^k 2k,则需填补后面的数位.

②若加上 2 k + 1 2^{k+1} 2k+1,则需填补 m = 2 k + 1 − n m=2^{k+1}-n m=2k+1n后面的数位,即将 n n n从该位开始的后缀的数码翻转.

​ 显然求 m m m无需确定 k k k值,直接翻转初始时的 n n n各数码即可.

v [ 0 ] = n , v [ 1 ] = m , d p [ i ] [ j ] v[0]=n,v[1]=m,dp[i][j] v[0]=n,v[1]=m,dp[i][j]表示构造 v [ i ] v[i] v[i]从下标 j j j开始的后缀所需的最少 2 2 2的幂次的个数.

下面字符串下标从 1 1 1开始,若 n [ i ] = 0 n[i]=0 n[i]=0,则 m [ i ] = 1 m[i]=1 m[i]=1.

状态转移方程:

n [ i ] = 0 n[i]=0 n[i]=0时, d p [ 0 ] [ i ] = d p [ 0 ] [ i − 1 ] , d [ 1 ] [ i ] = min ⁡ { d p [ 0 ] [ i − 1 ] , d p [ 1 ] [ i − 1 ] } + 1 dp[0][i]=dp[0][i-1],d[1][i]=\min\{dp[0][i-1],dp[1][i-1]\}+1 dp[0][i]=dp[0][i1],d[1][i]=min{dp[0][i1],dp[1][i1]}+1.

n [ i ] = 1 n[i]=1 n[i]=1时, d p [ 1 ] [ i ] = d p [ 1 ] [ i − 1 ] , d p [ 0 ] [ i ] = min ⁡ { d p [ 0 ] [ i − 1 ] , d p [ 1 ] [ i − 1 ] } + 1 dp[1][i]=dp[1][i-1],dp[0][i]=\min\{dp[0][i-1],dp[1][i-1]\}+1 dp[1][i]=dp[1][i1],dp[0][i]=min{dp[0][i1],dp[1][i1]}+1.

初始条件 d p [ 0 ] [ 1 ] = [ n [ 1 ] = 1 ] , d p [ 1 ] [ 1 ] = 1 dp[0][1]=[n[1]=1],dp[1][1]=1 dp[0][1]=[n[1]=1],dp[1][1]=1,后者是因为翻转整个串需要一个 2 2 2的幂次.

代码I

const int MAXN = 1e6 + 5;
char s[MAXN];
int dp[2][MAXN];  // v[0]=n,v[1]=m,dp[i][j]表示构造v[i]从下标j开始的后缀所需的最少2的幂次的个数

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

  int n = strlen(s + 1);
  dp[0][1] = s[1] == '1', dp[1][1] = 1;  // 初始条件
  for (int i = 2; i <= n; i++) {
    if (s[i] == '0') {
      dp[0][i] = dp[0][i - 1];
      dp[1][i] = min(dp[0][i - 1], dp[1][i - 1]) + 1;
    }
    else {
      dp[1][i] = dp[1][i - 1];
      dp[0][i] = min(dp[0][i - 1], dp[1][i - 1]) + 1;
    }
  }
  cout << dp[0][n];
}

int main() {
  solve();
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值