[Codeforces] number theory (R1600) Part.12

[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)=(ab,b);②令 ( a , b ) = ( a , ∣ a − b ∣ ) (a,b)=(a,|a-b|) (a,b)=(a,ab).给定一个整数 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  (1t1e4)组测试数据.每组测试数据输入三个整数 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  (1a,b,x1e18).

思路

不妨设 a > b a>b a>b.第一步能到达的状态是 ( a − b , b ) (a-b,b) (ab,b) ( a , a − b ) (a,a-b) (a,ab),其中后者下一步能到达的状态是 ( b , a − b ) (b,a-b) (b,ab) ( a , b ) (a,b) (a,b),显然 ( b , a − b ) (b,a-b) (b,ab) ( a − b , b ) (a-b,b) (ab,b)对答案的影响相同,故只需考虑 ( a , b ) → ( a − b , b ) (a,b)\rightarrow(a-b,b) (a,b)(ab,b).

( a − b , b ) (a-b,b) (ab,b)下一步能到达的状态是 ( a − 2 b , b ) (a-2b,b) (a2b,b) ( a − b , a − 2 b ) (a-b,a-2b) (ab,a2b).同理后者下一步能到达的状态与之前的状态等价,也只需考虑前者.

上述过程类似于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 ab<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) xa  (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 1n n n n个节点和编号 1 ∼ ( n − 1 ) 1\sim (n-1) 1(n1) ( n − 1 ) (n-1) (n1)条边组成的树.定义一棵树是好的,如果任意一条包含一条或两条边的路径的权值之和都是素数,其中路径不能经过同一节点两次.为每条边赋一个权值,使得该树是好的树,若无解输出 − 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  (1t1e4)组测试数据.每组测试数据第一行输入一个整数 n    ( 2 ≤ n ≤ 1 e 5 ) n\ \ (2\leq n\leq 1\mathrm{e}5) n  (2n1e5).接下来 ( n − 1 ) (n-1) (n1)行每行输入两个整数 u , v    ( 1 ≤ u , v ≤ n ) u,v\ \ (1\leq u,v\leq n) u,v  (1u,vn).数据保证给出的是一棵树,且所有测试数据的 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  (1t1e4)组测试数据.每组测试数据输入一个整数 n    ( 1 ≤ n ≤ 4 e 4 ) n\ \ (1\leq n\leq 4\mathrm{e}4) n  (1n4e4).

思路

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][j1]+dp[ipj][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  (1t1e4)组测试数据.每组测试数据第一行输入一个整数 n    ( 1 ≤ n ≤ 2 e 5 ) n\ \ (1\leq n\leq 2\mathrm{e}5) n  (1n2e5).第二行输入 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).数据保证所有测试数据的 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,无新题目.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值