[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 (−2e9≤A,B,C≤2e9,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,∣k∣≥4e91>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 (1≤a,b,x,y≤2e9).
思路
显然将 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 (2≤n≤50)的正整数序列,满足如下三个要求:①对 ∀ 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 n≥3时,取前三个数为 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=1∏50pi,取 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} xi−yi,xi−yi+1,⋯,xi−1,初始时 y i = 0 y_i=0 yi=0.
第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5).接下来 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 (1≤xi≤1e5,0≤yi≤i−1,1≤i≤n).
思路
用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 (0≤a,b≤1e9,1≤c≤1e7).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 x≤a,考察 r = 1 0 9 ⋅ x m o d c r=10^9\cdot x\ \mathrm{mod}\ c r=109⋅x mod c.遍历所有 x x x,若 c − r ≤ b c-r\leq b c−r≤b,则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) 1∼n (1≤n≤1e9)的所有全排列,求字典序第 k ( 1 ≤ k ≤ 1 e 9 ) k\ \ (1\leq k\leq 1\mathrm{e}9) k (1≤k≤1e9)小的全排列中有多少个好的数字的下标也是好的,排列的下标从 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 n≥13时必有解.
预处理出 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 1∼n中最大的 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 (1≤n≤1e6),问从 ( 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 (1≤n≤1e5)个元素 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 (1≤ai≤1e7), S 2 S_2 S2包含 m ( 1 ≤ m ≤ 1 e 5 ) m\ \ (1\leq m\leq 1\mathrm{e}5) m (1≤m≤1e5)个元素 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 (1≤bi≤1e7). 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[i−1]+a[i] (i≥2);②将 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 (1≤n≤2000,0≤k≤1e9).第二行输入 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 (0≤ai≤1e9).
思路I
设 B i j = [ i ≥ j ] B_{ij}=[i\geq j] Bij=[i≥j],则 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=1∑nBijaj.将 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)i−j+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=1∑i(bk)j(bl)i−j+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+i−1k−1.
[证] ( 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=1∑kCk+j−1k−1=Ckk+Ckk−1+j=3∑iCk+j−1k−1=Ckk+1+Ck+1k−1+j=4∑iCk+j−1k=⋯=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+i−1(bk)i−1,递推求即可.
另一做法: 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=1∑iCk+i−ji−jaj.因 k k k的范围较大,无法预处理出所有组合数.注意到 i − j ≤ n ≤ 2000 i-j\leq n\leq 2000 i−j≤n≤2000,可只处理出所有 ( i − j ) (i-j) (i−j)可能的取值对应的组合数,总时间复杂度 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} p∈R,若 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,⋯,an−1,设其生成函数为 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} 1−x1.
[证] 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+⋯=1−x1 (∣x∣<1).
设 F ( x ) = ∑ n ∞ f n x n \displaystyle F(x)=\sum_n^\infty f_nx^n F(x)=n∑∞fnxn,则 F ( x ) = 1 ( 1 − x ) k F(x)=\dfrac{1}{(1-x)^k} F(x)=(1−x)k1.
由引理: ( 1 − x ) F ′ ( x ) = − k F ( x ) ⋅ ( − 1 ) = k F ( x ) \displaystyle (1-x)F'(x)=-kF(x)\cdot (-1)=kF(x) (1−x)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+1xn−n=1∑∞nfnxn=kn=0∑∞fnxn.
则 { 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)=kf0⇒f1=k(n+1)fn+1−nfn=kfn (n≥1),故 f n = n + k − 1 n f n − 1 f_n=\dfrac{n+k-1}{n}f_{n-1} fn=nn+k−1fn−1,由此可见 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 n≤2000,暴力求即可.
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+⋯+2k−1<2k,则为使得 s s s的最高位为 1 1 1,需加上某个 2 x ( x ≥ k ) 2^x\ \ (x\geq k) 2x (x≥k).注意到加上 x ≥ k + 2 x\geq k+2 x≥k+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+1−n后面的数位,即将 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][i−1],d[1][i]=min{dp[0][i−1],dp[1][i−1]}+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][i−1],dp[0][i]=min{dp[0][i−1],dp[1][i−1]}+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();
}