[Codeforces] number theory (R1600) Part.3
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
346A. Alice and Bob
原题指路:https://codeforces.com/problemset/problem/346/A
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
Alice和Bob两人玩游戏,Alice先手.初始时给定一个整数集合 S S S.每轮玩家选两个相异的整数 x , y ∈ S x,y\in S x,y∈S,使得 ∣ x − y ∣ ∉ S |x-y|\not\in S ∣x−y∣∈S,并将 ∣ x − y ∣ |x-y| ∣x−y∣加入 S S S中,不能操作者负.问最后谁赢.
第一行输入一个整数 n ( 2 ≤ n ≤ 100 ) n\ \ (2\leq n\leq 100) n (2≤n≤100).第二行输入 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 (1≤ai≤1e9).
思路
类似于闭包,无论中间过程,最后得到的集合都相同.设 g = gcd 1 ≤ i ≤ n a i \displaystyle g=\gcd_{1\leq i\leq n}a_i g=1≤i≤ngcdai,则最终集合为 { d , 2 d , 3 d , ⋯ , max { x i } } \{d,2d,3d,\cdots,\max\{x_i\}\} {d,2d,3d,⋯,max{xi}},故轮数为 max { x i } d \dfrac{\max\{x_i\}}{d} dmax{xi},判断其奇偶性即可.
代码
void solve() {
int n; cin >> n;
valarray<int> a(n);
int g = 0; // gcd
for (auto& ai : a) {
cin >> ai;
g = gcd(g, ai);
}
int ans = a.max() / g - n;
cout << (ans & 1 ? "Alice" : "Bob");
}
int main() {
solve();
}
353C. Find Maximum
原题指路:https://codeforces.com/problemset/problem/353/C
题意
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.定义函数 f ( x ) = ∑ i = 0 n − 1 a i ⋅ b i t ( i ) \displaystyle f(x)=\sum_{i=0}^{n-1} a_i\cdot bit(i) f(x)=i=0∑n−1ai⋅bit(i),它以 [ 0 , 2 n − 1 ] [0,2^n-1] [0,2n−1]中的一个整数为参数 x x x,其中 b i t ( i ) = 1 bit(i)=1 bit(i)=1当且仅当参数 x x x的二进制表示第 i i i位为 1 1 1;否则 b i t ( i ) = 0 bit(i)=0 bit(i)=0.如 n = 4 , x = 11 = 2 0 + 2 1 + 2 3 n=4,x=11=2^0+2^1+2^3 n=4,x=11=20+21+23时, f ( x ) = a 0 + a 1 + a 3 f(x)=a_0+a_1+a_3 f(x)=a0+a1+a3.给定一个整数 m m m,求 f ( x ) f(x) f(x)在 x ∈ [ 0 , m ] x\in[0,m] x∈[0,m]上的最大值.
第一行输入一个整数 n ( 1 ≤ n ≤ 1 e 5 ) n\ \ (1\leq n\leq 1\mathrm{e}5) n (1≤n≤1e5).第二行输入 n n n个整数 a 1 , ⋯ , a n ( 0 ≤ a i ≤ 1 e 4 ) a_1,\cdots,a_n\ \ (0\leq a_i\leq 1\mathrm{e}4) a1,⋯,an (0≤ai≤1e4).第三行输入一个长度为 n n n的 0 − 1 0-1 0−1串,表示整数 m m m的二进制表示.
思路I
①若 m m m的MSB为 0 0 0,则 ∀ x ∈ [ 0 , m ] \forall x\in[0,m] ∀x∈[0,m]的二进制表示的第 ( n − 1 ) (n-1) (n−1)位为 0 0 0,则 a n − 1 a_{n-1} an−1不会出现在 f ( x ) f(x) f(x)中.
②若 m m m的MSB为 1 1 1,则对某些 x x x, a n − 1 a_{n-1} an−1会出现在 f ( x ) f(x) f(x)中.考察不包含 a n − 1 a_{n-1} an−1的 f ( x ) f(x) f(x),显然此时 x ∈ [ 0 , 2 n − 1 − 1 ] x\in[0,2^{n-1}-1] x∈[0,2n−1−1].
故 x = 2 n − 1 − 1 x=2^{n-1}-1 x=2n−1−1时 f ( x ) f(x) f(x)取得最大值.
从高位到地位枚举 m m m的二进制表示的数位,数位为 1 1 1时更新答案即可,过程用前缀和、后缀和加速.
代码I
const int MAXN = 1e5 + 5;
int n;
int a[MAXN];
int pre[MAXN]; // a[]的前缀和
string s;
void solve() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
pre[i + 1] = pre[i] + a[i];
}
cin >> s;
int ans = 0;
for (int i = n - 1, suf = 0; i >= 0; i--) // suf为a[]的后缀和
if (s[i] == '1') ans = max({ ans,pre[i] + suf,suf += a[i] });
cout << ans;
}
int main() {
solve();
}
371B. Fox Dividing Cheese
原题指路:https://codeforces.com/problemset/problem/371/B
题意
给定两整数 a , b a,b a,b.现有如下三种操作,选定一个数 x x x:①若 2 ∣ x 2\mid x 2∣x,令 x − = x 2 x-=\dfrac{x}{2} x−=2x;②若 3 ∣ x 3\mid x 3∣x,令 x − = 2 3 x x-=\dfrac{2}{3}x x−=32x;③若 5 ∣ x 5\mid x 5∣x,令 x − = 4 5 x x-=\dfrac{4}{5}x x−=54x.若经若干次操作后能使得 a = b a=b a=b,输出最小操作次数;否则输出 − 1 -1 −1.
思路
显然操作①等价于 x / = 2 x/=2 x/=2,操作②等价于 x / = 3 x/=3 x/=3,操作③等价于 x / = 5 x/=5 x/=5.
设 a = x ⋅ 2 α 1 ⋅ 3 α 2 ⋅ 5 α 3 , b = y ⋅ 2 β 1 ⋅ 3 β 2 ⋅ 5 β 3 a=x\cdot 2^{\alpha_1}\cdot 3^{\alpha_2}\cdot 5^{\alpha_3},b=y\cdot 2^{\beta_1}\cdot 3^{\beta_2}\cdot 5^{\beta_3} a=x⋅2α1⋅3α2⋅5α3,b=y⋅2β1⋅3β2⋅5β3.
①若 x ≠ y x\neq y x=y,显然无解.
②若 x = y x=y x=y,则 a n s = ∣ α 1 − β 1 ∣ + ∣ α 2 − β 2 ∣ + ∣ α 3 − β 3 ∣ ans=|\alpha_1-\beta_1|+|\alpha_2-\beta_2|+|\alpha_3-\beta_3| ans=∣α1−β1∣+∣α2−β2∣+∣α3−β3∣.
代码
vi get(int n) {
vi res(4, 0); // 素因子2的次数、3的次数、5的次数、余下的因子之积
while (n % 2 == 0) {
n /= 2;
res[0]++;
}
while (n % 3 == 0) {
n /= 3;
res[1]++;
}
while (n % 5 == 0) {
n /= 5;
res[2]++;
}
res[3] = n;
return res;
}
void solve() {
int a, b; cin >> a >> b;
auto res1 = get(a), res2 = get(b);
if (res1[3] != res2[3]) cout << -1;
else cout << abs(res1[0] - res2[0]) + abs(res1[1] - res2[1]) + abs(res1[2] - res2[2]);
}
int main() {
solve();
}
375A. Divisible by Seven
原题指路:https://codeforces.com/problemset/problem/375/A
题意
给定一个至少包含包含数码 1 , 6 , 8 , 9 1,6,8,9 1,6,8,9的整数 a a a,重排其数码使得其能被 7 7 7整除,输出重排后的数字(无前导零),无解输出 0 0 0.
思路
考察 1 , 6 , 8 , 9 1,6,8,9 1,6,8,9是否能拼出模 7 7 7后余数为 0 ∼ 6 0\sim 6 0∼6的所有数.枚举知: 1869 % 7 = 0 , 8961 % 7 = 1 , 1689 % 7 = 2 , 6198 % 7 = 3 , 1698 % 7 = 4 , 9861 % 7 = 5 , 1896 % 7 = 6 1869\% 7=0,8961\% 7=1,1689\% 7=2,6198\% 7=3,1698\% 7=4,9861\% 7=5,1896\% 7=6 1869%7=0,8961%7=1,1689%7=2,6198%7=3,1698%7=4,9861%7=5,1896%7=6.故前面的数码任意排,后面补上对应余数的 1 , 6 , 8 , 9 1,6,8,9 1,6,8,9的排列即可.
因不输出前导零,将所有 0 0 0放在最后输出即可,显然这不影响余数.
代码
const int MAXN = 10;
const int last[] = { 1869,8961,1689,6198,1698,9861,1896,1869 };
int cnt[MAXN]; // 每个数码出现的次数
void solve() {
string s; cin >> s;
for (auto ch : s) cnt[ch & 15]++;
cnt[1]--, cnt[6]--, cnt[8]--, cnt[9]--;
int r = 0; // 余数
for (int i = 1; i < 10; i++) {
while (cnt[i]--) {
r = (r * 10 + i) % 7;
cout << i;
}
}
cout << last[7 - r * 10000 % 7];
while (cnt[0]--) cout << 0;
}
int main() {
solve();
}
414A. Mashmokh and Numbers
原题指路:https://codeforces.com/problemset/problem/414/A
题意
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作:若当前序列元素个数 ≥ 2 \geq 2 ≥2,取序列的前两个元素 x , y x,y x,y,将它们删去,同时获得 gcd ( x , y ) \gcd(x,y) gcd(x,y)的分数.构造一个元素相异的序列$a_1,\cdots,a_n\ \ (1\leq a_i\leq 1\mathrm{e}9)\ s.t.\ 操作后总得分为 操作后总得分为 操作后总得分为k . 无解输出 .无解输出 .无解输出-1$.
第一行输入两个整数 n , k ( 1 ≤ n ≤ 1 e 5 , 0 ≤ k ≤ 1 e 8 ) n,k\ \ (1\leq n\leq 1\mathrm{e}5,0\leq k\leq 1\mathrm{e}8) n,k (1≤n≤1e5,0≤k≤1e8).
思路
显然每次操作至少获得 1 1 1分,则 k < ⌊ n 2 ⌋ k<\left\lfloor\dfrac{n}{2}\right\rfloor k<⌊2n⌋时无解.
k ≥ n 2 k\geq \dfrac{n}{2} k≥2n时,考察 x = k − ⌊ n − 2 2 ⌋ x=k-\left\lfloor\dfrac{n-2}{2}\right\rfloor x=k−⌊2n−2⌋,取 a 1 = x , a 2 = 2 x a_1=x,a_2=2x a1=x,a2=2x,其余元素取一个连续的升序序列即可.
注意特判 n = 1 n=1 n=1的情况,此时有解当且仅当 k = 0 k=0 k=0.
代码
void solve() {
int n, k; cin >> n >> k;
if (n == 1) cout << (k ? -1 : 1);
else if (k < n / 2) cout << -1;
else {
int x = k - (n - 2) / 2;
cout << x << ' ' << 2 * x << ' ';
for (int i = 2 * x + 1; i <= 2 * x + n - 2; i++) cout << i << ' ';
}
}
int main() {
solve();
}
414B. Mashmokh and ACM
原题指路:https://codeforces.com/problemset/problem/414/B
题意
称一个整数序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an是好的,如果 a i ∣ a i + 1 ( 1 ≤ i ≤ n − 1 ) a_i\mid a_{i+1}\ \ (1\leq i\leq n-1) ai∣ai+1 (1≤i≤n−1).给定整数 n , k ( 1 ≤ n , k ≤ 2000 ) n,k\ \ (1\leq n,k\leq 2000) n,k (1≤n,k≤2000),求长度为 k k k的、元素范围为 [ 1 , n ] [1,n] [1,n]的好的整数序列的个数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7取模.
思路
d p [ i ] [ j ] dp[i][j] dp[i][j]表示长度为 i i i的、以 j j j结尾的好的序列的个数.枚举 j j j的约数 d d d,状态转移方程 d p [ i ] [ j ] = ∑ d ∣ x d p [ i − 1 ] [ d ] \displaystyle dp[i][j]=\sum_{d\mid x}dp[i-1][d] dp[i][j]=d∣x∑dp[i−1][d].初始条件可取 d p [ 0 ] [ 1 ] = 1 dp[0][1]=1 dp[0][1]=1.
暴力转移时间复杂度 O ( k n n ) O(kn\sqrt{n}) O(knn),会TLE.考虑优化,先预处理出 2000 2000 2000内所有数的约数,总时间复杂度 O ( k n log n ) O(kn\log n) O(knlogn).
代码
const int MAXN = 2005;
const int MOD = 1e9 + 7;
vi divisors[MAXN]; // 存每个数的约数
int dp[MAXN][MAXN]; // dp[i][j]表示长度为i的、以j结尾的好的序列的个数
void init() {
for (int i = 1; i < MAXN; i++)
for (int j = i; j < MAXN; j += i) divisors[j].push_back(i);
}
void solve() {
init();
int n, k; cin >> n >> k;
dp[0][1] = 1;
for (int i = 1; i <= k; i++) {
for (int j = 1; j <= n; j++) {
for (auto d : divisors[j])
dp[i][j] = ((ll)dp[i][j] + dp[i - 1][d]) % MOD;
}
}
int ans = 0;
for (int i = 1; i <= n; i++) ans = ((ll)ans + dp[k][i]) % MOD;
cout << ans;
}
int main() {
solve();
}
460B. Little Dima and Equation
原题指路:https://codeforces.com/problemset/problem/460/B
题意
给定系数 a , b , c ( 1 ≤ a ≤ 5 , 1 ≤ b ≤ 1 e 4 , − 1 e 4 ≤ c ≤ 1 e 4 ) a,b,c\ \ (1\leq a\leq 5,1\leq b\leq 1\mathrm{e}4,-1\mathrm{e}4\leq c\leq 1\mathrm{e}4) a,b,c (1≤a≤5,1≤b≤1e4,−1e4≤c≤1e4),求方程 x = b ⋅ s ( x ) a + c x=b\cdot s(x)^a+c x=b⋅s(x)a+c在 x ∈ ( 0 , 1 e 9 ) x\in(0,1\mathrm{e}9) x∈(0,1e9)中的所有整数解,其中 s ( x ) s(x) s(x)表示 x x x的十进制表示的数位之和.第一行输出解的个数,第二行升序输出所有解.
思路
注意到对 ∀ x ∈ ( 0 , 1 e 9 ) , s ( x ) ∈ [ 1 , 81 ] \forall x\in(0,1\mathrm{e}9),s(x)\in[1,81] ∀x∈(0,1e9),s(x)∈[1,81],故可枚举 s s s,求出对应的 x x x后,判断是否 s ( x ) = s s(x)=s s(x)=s且 x < 1 e 9 x<1\mathrm{e}9 x<1e9即可.
代码
int get_sum(int x) {
int res = 0;
while (x) {
res += x % 10;
x /= 10;
}
return res;
}
ll qpow(int a, int k) {
ll res = 1;
while (k) {
if (k & 1) res = res * a;
k >>= 1;
a = a * a;
}
return res;
}
void solve() {
int a, b, c; cin >> a >> b >> c;
vi ans;
for (int s = 1; s <= 81; s++) {
ll x = qpow(s, a) * b + c;
if (get_sum(x) == s && x < 1e9) ans.push_back(x);
}
cout << ans.size() << endl;
for (auto i : ans) cout << i << ' ';
}
int main() {
solve();
}
495B. Modular Equations
原题指路:https://codeforces.com/problemset/problem/495/B
题意
给定整数 a , b ( 0 ≤ a , b ≤ 1 e 9 ) a,b\ \ (0\leq a,b\leq 1\mathrm{e}9) a,b (0≤a,b≤1e9),求关于 x x x的方程 a m o d x = b a\ \mathrm{mod}\ x=b a mod x=b的解数,无解输出 0 0 0,有无穷多组解输出"infinity".
思路
① a < b a<b a<b时,显然无解.
② a = b a=b a=b时, ∀ x > a \forall x>a ∀x>a都是方程的解.
③ a > b a>b a>b时,显然 x ∣ ( a − b ) x\mid (a-b) x∣(a−b),且 x > b x>b x>b.故方程的解数即 ( a − b ) (a-b) (a−b)的 > b >b >b的约数个数.
代码
void solve() {
int a, b; cin >> a >> b;
if (a < b) cout << 0;
else if (a == b) cout << "infinity";
else {
int c = a - b;
int ans = 0;
for (int i = 1; i <= c / i; i++)
if (c % i == 0) ans += (i > b) + (i != c / i && c / i > b);
cout << ans;
}
}
int main() {
solve();
}
515B. Drazil and His Happy Friends
原题指路:https://codeforces.com/problemset/problem/515/B
题意 ( 2 s 2\ \mathrm{s} 2 s)
有 n n n个男生和 m m m个女生,分别编号 0 ∼ ( n − 1 ) 0\sim (n-1) 0∼(n−1)和 0 ∼ ( m − 1 ) 0\sim (m-1) 0∼(m−1).初始时他们之中有些人开心,有些人不开心.现第 i i i天会邀请编号 ( i m o d n ) (i\ \mathrm{mod}\ n) (i mod n)的男生和编号 ( i m o d m ) (i\ \mathrm{mod}\ m) (i mod m)的女生吃饭,若两人中有一人开心,则两人都会永久地开心.问最后能否使得所有人开心,若能则输出"Yes";否则输出"No".
第一行输入两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m\ \ (1\leq n,m\leq 100) n,m (1≤n,m≤100).第二行先输入一个整数 b ( 0 ≤ b ≤ n ) b\ \ (0\leq b\leq n) b (0≤b≤n).接下来输入 b b b个整数 x 1 , ⋯ , x b ( 0 ≤ x i < n ) x_1,\cdots,x_b\ \ (0\leq x_i<n) x1,⋯,xb (0≤xi<n),表示初始时开心的男生.第三行先输入一个整数 g ( 0 ≤ g ≤ m ) g\ \ (0\leq g\leq m) g (0≤g≤m).接下来输入 g g g个整数 y 1 , ⋯ , y b ( 0 ≤ y i < m ) y_1,\cdots,y_b\ \ (0\leq y_i<m) y1,⋯,yb (0≤yi<m),表示初始时开心的女生.
思路
显然至多 n m nm nm天会出现循环,若 n m nm nm天后还有人不开心,则答案为No.可模拟前 n m nm nm天的情况,时间复杂度 O ( n m ) O(nm) O(nm).
事实上本题有 O ( n + m ) O(n+m) O(n+m)的解法.设 g = gcd ( n , m ) g=\gcd(n,m) g=gcd(n,m).若 i i i号人开心,则编号为 x ≡ i ( m o d g ) x\equiv i\ \ (\mathrm{mod}\ g) x≡i (mod g)的人都会变开心,则只需考察编号 m o d g \mathrm{mod}\ g mod g的每个余数中是否至少有一个人开心即可.
代码
const int MAXN = 105;
bool happy[MAXN];
void solve() {
int n, m; cin >> n >> m;
int g = gcd(n, m);
int xn; cin >> xn;
while (xn--) {
int x; cin >> x;
happy[x % g] = true;
}
int yn; cin >> yn;
while (yn--) {
int y; cin >> y;
happy[y % g] = true;
}
for (int i = 0; i < g; i++) {
if (!happy[i]) {
cout << "No";
return;
}
}
cout << "Yes";
}
int main() {
solve();
}
519C. A and B and Team Training
原题指路:https://codeforces.com/problemset/problem/519/C
题意
一支队伍有如下两种组成方式:① 1 1 1个A类人和 2 2 2个B类人;② 2 2 2个A类人和 1 1 1个B类人.现有 n n n个A类人和 m ( 0 ≤ n , m ≤ 5 e 5 ) m\ \ (0\leq n,m\leq 5\mathrm{e}5) m (0≤n,m≤5e5)个B类人,求最多能组成多少支队伍.
思路
设组成①类队 i ( 0 ≤ i ≤ n ) i\ \ (0\leq i\leq n) i (0≤i≤n)支,则剩余 ( n − i ) (n-i) (n−i)个A类人和 ( m − 2 i ) (m-2i) (m−2i)个B类人,他们能组成②类队 min { n − i 2 , m − 2 i } \min\left\{\dfrac{n-i}{2},m-2i\right\} min{2n−i,m−2i}.枚举每个 i i i,更新答案即可.
代码
void solve() {
int n, m; cin >> n >> m;
int ans = 0;
for (int i = 0; i <= n; i++) { // 枚举①类队队伍数
int res = i;
if (m - 2 * i >= 0) {
res += min((n - i) / 2, m - 2 * i);
ans = max(ans, res);
}
}
cout << ans;
}
int main() {
solve();
}