[Codeforces] number theory (R1600) Part.12
题单:https://codeforces.com/problemset/page/1?tags=number+theory%2C1201-1600
1612D. X-Magic Pair
原题指路:https://codeforces.com/problemset/problem/1612/D
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个整数对 ( a , b ) (a,b) (a,b).现有两种操作:①令 ( a , b ) = ( ∣ a − b ∣ , b ) (a,b)=(|a-b|,b) (a,b)=(∣a−b∣,b);②令 ( a , b ) = ( a , ∣ a − b ∣ ) (a,b)=(a,|a-b|) (a,b)=(a,∣a−b∣).给定一个整数 x x x,问若干次(可能为零次)操作后能否 s . t . a = x \ s.t.\ a=x s.t. a=x或 b = x b=x b=x.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入三个整数 a , b , x ( 1 ≤ a , b , x ≤ 1 e 18 ) a,b,x\ \ (1\leq a,b,x\leq 1\mathrm{e}18) a,b,x (1≤a,b,x≤1e18).
思路
不妨设 a > b a>b a>b.第一步能到达的状态是 ( a − b , b ) (a-b,b) (a−b,b)和 ( a , a − b ) (a,a-b) (a,a−b),其中后者下一步能到达的状态是 ( b , a − b ) (b,a-b) (b,a−b)和 ( a , b ) (a,b) (a,b),显然 ( b , a − b ) (b,a-b) (b,a−b)与 ( a − b , b ) (a-b,b) (a−b,b)对答案的影响相同,故只需考虑 ( a , b ) → ( a − b , b ) (a,b)\rightarrow(a-b,b) (a,b)→(a−b,b).
( a − b , b ) (a-b,b) (a−b,b)下一步能到达的状态是 ( a − 2 b , b ) (a-2b,b) (a−2b,b)和 ( a − b , a − 2 b ) (a-b,a-2b) (a−b,a−2b).同理后者下一步能到达的状态与之前的状态等价,也只需考虑前者.
上述过程类似于Euclid算法求 gcd \gcd gcd,即一直让 a − = b a-=b a−=b直至 a < b a<b a<b,亦即 a % = b a\%=b a%=b.对 a − b < b a-b<b a−b<b的情况,交换整数对的两项后继续递归即可.递归过程中检查是否 a = x a=x a=x或 b = x b=x b=x或 a > x a>x a>x且 x ≡ a ( m o d b ) x\equiv a\ \ (\mathrm{mod}\ b) x≡a (mod b),递归终止条件为 a = 0 a=0 a=0或 b = 0 b=0 b=0或 a < x a<x a<x.
代码
bool dfs(ll a, ll b, ll x) {
if (a < b) swap(a, b);
if (a == x || b == x) return true;
if (!a || !b || a < x) return false;
if (x % b == a % b) return true;
return dfs(a % b, b, x);
}
void solve() {
ll a, b, x; cin >> a >> b >> x;
cout << (dfs(a, b, x) ? "YES" : "NO") << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1627C. Not Assigning
原题指路:https://codeforces.com/problemset/problem/1627/C
题意 ( 1.5 s ) (1.5\ \mathrm{s}) (1.5 s)
给定一棵包含编号 1 ∼ n 1\sim n 1∼n的 n n n个节点和编号 1 ∼ ( n − 1 ) 1\sim (n-1) 1∼(n−1)的 ( n − 1 ) (n-1) (n−1)条边组成的树.定义一棵树是好的,如果任意一条包含一条或两条边的路径的权值之和都是素数,其中路径不能经过同一节点两次.为每条边赋一个权值,使得该树是好的树,若无解输出 − 1 -1 −1.可以证明若有解,则存在权值在 [ 1 , 1 e 5 ] [1,1\mathrm{e}5] [1,1e5]范围内的解.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数 n ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n (2≤n≤1e5).接下来 ( n − 1 ) (n-1) (n−1)行每行输入两个整数 u , v ( 1 ≤ u , v ≤ n ) u,v\ \ (1\leq u,v\leq n) u,v (1≤u,v≤n).数据保证给出的是一棵树,且所有测试数据的 n n n之和不超过 1 e 5 1\mathrm{e}5 1e5.
思路
若有解,则树是链.
[证] 若不然,则至少存在一个点的度数 > 2 >2 >2,不妨设该点的度数为 3 3 3.设该点所连的三条边的权值分别为 a , b , c a,b,c a,b,c.
① a , b , c a,b,c a,b,c中至多有一个 2 2 2,否则存在一条包含两条边的路径,其权值之和为 2 + 2 = 4 2+2=4 2+2=4,是合数.故 a , b , c a,b,c a,b,c中有两个奇素数.
②设 a , b , c a,b,c a,b,c中前两个数为奇素数,最后一个数为 2 2 2,则 a + b a+b a+b是 ≥ 6 \geq 6 ≥6的偶数,不符合条件.
对链树,给链上的边交替地赋边权 2 2 2和 3 3 3即可.
代码
const int MAXN = 1e5 + 5;
int n;
vii edges[MAXN]; // 下一个顶点、边的编号
int d[MAXN]; // 节点的度数
int ans[MAXN];
void dfs(int u, int fa, int cur) { // 当前节点、前驱节点、当前用到的数为(cur+2)
for (auto [v, idx] : edges[u]) {
if (v == fa) continue;
ans[idx] = cur + 2;
dfs(v, u, cur ^ 1);
}
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
edges[i].clear();
d[i] = 0;
}
bool ok = true;
for (int i = 1; i < n; i++) {
int u, v; cin >> u >> v;
edges[u].push_back({ v,i }), edges[v].push_back({ u,i });
d[u]++, d[v]++;
ok &= (d[u] <= 2 && d[v] <= 2);
}
if (!ok) cout << -1 << endl;
else {
int s = 0; // 链的顶点
for (int i = 1; i <= n; i++) {
if (d[i] == 1) {
s = i;
break;
}
}
dfs(s, -1, 0); // 从链的顶点开始搜,无前驱节点,当前用到的数为2+0
for (int i = 1; i < n; i++) cout << ans[i] << " \n"[i == n - 1];
}
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
1673C. Palindrome Basis
原题指路:https://codeforces.com/problemset/problem/1673/C
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个正整数 n n n,将其表示为若干个回文的正整数之和,求方案数,答案对 1 e 9 + 7 1\mathrm{e}9+7 1e9+7.两方案视为不同当且仅当方案中至少存在一个回文数不同.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据输入一个整数 n ( 1 ≤ n ≤ 4 e 4 ) n\ \ (1\leq n\leq 4\mathrm{e}4) n (1≤n≤4e4).
思路
4 e 4 4\mathrm{e}4 4e4内的回文数个数很少.为求五位的回文数的个数,只需遍历 ≤ 400 \leq 400 ≤400的三位数作为前三位,再将前两位沿第三位对称到后两位.可以求得五位的回文数有 300 300 300个,四位的回文数有 90 90 90个,三位的回文数有 90 90 90个,两位的回文数有 9 9 9个,一位的回文数有 9 9 9个,故 4 e 4 4\mathrm{e}4 4e4内的回文数共 m = 498 m=498 m=498个.
预处理出 4 e 4 4\mathrm{e}4 4e4内的回文数 p 1 , ⋯ , p m p_1,\cdots,p_m p1,⋯,pm. d p [ i ] [ j ] dp[i][j] dp[i][j]表示只用前 j j j个回文数凑出数 i i i的方案数,则 a n s = d p [ n ] [ m ] ans=dp[n][m] ans=dp[n][m].
状态转移方程 d p [ i ] [ j ] = d p [ i ] [ j − 1 ] + d p [ i − p j ] [ j ] dp[i][j]=dp[i][j-1]+dp[i-p_j][j] dp[i][j]=dp[i][j−1]+dp[i−pj][j],初始条件 d p [ i ] [ 1 ] = d p [ 1 ] [ j ] = 1 dp[i][1]=dp[1][j]=1 dp[i][1]=dp[1][j]=1.
代码
const int MAXN = 4e4 + 5, MAXM = 500;
const int MOD = 1e9 + 7;
int n;
int p[MAXM], cnt;
int dp[MAXN][MAXM]; // dp[i][j]表示只用前j个回文数凑出数i的方案数
bool check(int n) { // 判断
string s = to_string(n), tmps = s;
reverse(all(s));
return tmps == s;
}
void init() { // 预处理出4e4内的回文数
for (int i = 1; i < MAXN; i++)
if (check(i)) p[cnt++] = i;
for (int j = 0; j < cnt; j++) dp[0][j] = 1; // 初始条件
for (int i = 1; i < MAXN; i++) {
dp[i][0] = 0; // 初始条件
for (int j = 0; j < cnt; j++) {
if (p[j] > i) dp[i][j] = dp[i][j - 1];
else dp[i][j] = (dp[i][j - 1] + dp[i - p[j]][j]) % MOD;
}
}
}
void solve() {
cin >> n;
cout << dp[n][cnt - 1] << endl;
}
int main() {
init();
CaseT // 单测时注释掉该行
solve();
}
1714E. Add Modulo 10
原题指路:https://codeforces.com/problemset/problem/1714/E
题意 ( 2 s ) (2\ \mathrm{s}) (2 s)
给定一个长度为 n n n的序列 a 1 , ⋯ , a n a_1,\cdots,a_n a1,⋯,an.现有操作:选一个下标 i i i,令 a i + = a i m o d 10 a_i+=a_i\ \mathrm{mod}\ 10 ai+=ai mod 10.问经若干次操作能否使得所有 a i a_i ai都相等.
有 t ( 1 ≤ t ≤ 1 e 4 ) t\ \ (1\leq t\leq 1\mathrm{e}4) t (1≤t≤1e4)组测试数据.每组测试数据第一行输入一个整数 n ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n (1≤n≤2e5).第二行输入 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).数据保证所有测试数据的 n n n之和不超过 2 e 5 2\mathrm{e}5 2e5.
思路
设 a i a_i ai的末位为 d d d.考察操作对 a i a_i ai的影响.
①若 d = 0 d=0 d=0,则操作不改变 a i a_i ai,此时有解当且仅当初始时所有 a i a_i ai都相等.
②若 d d d为非零偶数,则会出现 2 , 4 , 8 , 6 2,4,8,6 2,4,8,6的循环,每循环一次 a i a_i ai增大 20 20 20.
③若 d d d为奇数,则进行一次操作后转化为②.
对情况②和③,可不断进行操作直至 d = 2 d=2 d=2,然后检查每一项模 20 20 20的余数是否相等.
代码
void solve() {
int n; cin >> n;
vi a(n);
bool zero = false;
for (auto& ai : a) {
cin >> ai;
while (ai % 10 && ai % 10 != 2) ai += ai % 10;
zero |= ai % 10 == 0;
}
if (zero) {
cout << (a == vi(n, a[0]) ? "YES" : "NO") << endl;
return;
}
bool ok = true;
int target = a[0] % 20;
for (int i = 1; i < n; i++) ok &= a[i] % 20 == target;
cout << (ok ? "YES" : "NO") << endl;
}
int main() {
CaseT // 单测时注释掉该行
solve();
}
截至2022.10.2,无新题目.