Educational Codeforces Round 139 (A-D)

A

题意:有t组测试数据,每组测试数据输入一个整数n,输出一个整数,表示[1, n]范围内满足以下条件的x的个数。其中x满足条件"只有一个非0的位数(比如5000, 4, 200)"。

解法/思路:举个例子,若n为四位数,则必然有9个一位数,9个二位数,9个三位数满足条件,而满足条件的四位数就等于n的首位数的值。

代码:

#include <iostream>

using namespace std;

int main() {
  int t, n, ans = 0;
  cin >> t;

  while (t--) {
    cin >> n;
    while (n >= 10) {
      n /= 10;
      ans += 9;
    }
    ans += n;
    cout << ans << endl;
    ans = 0;
  }

  return 0;
}

 

B

题意:t组测试数据,每组测试数据输入共两行,第一行输入整数n,第二行输入长度为n的字符串。输出共一行,若该字符串能在操作次数小于n的情况下打出来,则输出"YES",否则输出"NO"。

所谓"操作"有两种:
1.在字符串末端打出一个字符。
2.复制一段前面已经打出来的部分追加到字符串末端。

显然,只要字符串当中出现两个长度为2,完全相同且位置不重合(比如"hhh"中,前面的"hh"和后面的"hh"就是重合了)的子串,就可以断定为"YES"。

解法/思路:本人采用unordered_map(STL大法好( •̀ ω •́ )y),记录所有上述子串及其出现的位置,竟然意外地AC了。

不过其实 unordered_map 完全可以不用,两个小写字母所组成的字符串最多也就 26 * 26 = 676 种,用一个长度为 676 的数组记录即可。

代码:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

int main() {
  int t, len, k = 0;
  string str, tmp;
  cin >> t;

  while (t--) {
    unordered_map<string, int> mp;
    cin >> len;
    cin >> str;
    for (k = 0; k < len - 1; ++k) {
      tmp = str.substr(k, 2);
      if (mp.find(tmp) != mp.end() && k - mp[tmp] > 1) {  // 如果该子串出现过且不"重合"
        cout << "YES" << endl;
        break;
      } else {
        mp.insert(make_pair(tmp, k));
      }
    }
    if (k == len - 1) {
      cout << "NO" << endl;
    }
  }

  return 0;
}

 

C

题意:有两排砖块,它们组成一个矩形,原本所有砖块都是白色,现在要求用一只刷子刷上黑色油漆,使得每一列至少有一块是黑的,刷子只能上下左右地移动,且已经刷过的地方就不能再经过了,现在有t组测试数据,每组测试数据输入共三行,第一行是一个整数n,下面两行是两个长度为n,仅由'W'和'B'组成的字符串('B'即表示"Black"),输出共1行,若能够刷成这两个字符串所表示的样子,输出"YES",否则输出"NO"。

解法/思路:找规律。当两个靠的最近的'W'相差的列数为偶数时,如果它们在同一列;或者如果相差的列数为奇数,且不在同一列,则为"NO"。

代码:

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>

using namespace std;

char r1[200001];
char r2[200001];
int x, y = 0, x2, y2 = 0;  // x, y, x2, y2用于记录'W'出现的位置,每次遇到'W'时,更新y小的那组数据

int main() {
  int t, n, k;
  cin >> t;
  while (t--) {
    cin >> n;
    scanf("%s", r1 + 1);
    scanf("%s", r2 + 1);
    for (k = 1; k <= n; ++k) {
      if (r1[k] == 'W') {
        if (y < y2) {
          y = k;
          x = 1;
        } else {
          y2 = k;
          x2 = 1;
        }
      }
      if (r2[k] == 'W') {
        if (y < y2) {
          y = k;
          x = 2;
        } else {
          y2 = k;
          x2 = 2;
        }
      }
      if (y && y2) {
        if (x != x2 && ((abs(y2 - y)) % 2 != 0)) {
          cout << "NO" << endl;
          break;
        }
        if (x == x2 && ((abs(y2 - y)) % 2 == 0)) {
          cout << "NO" << endl;
          break;
        }
      }
    }
    if (k == n + 1) {
      cout << "YES" << endl;
    }
    y = 0;
    y2 = 0;
  }
  return 0;
}

 

D

题意:如果二元组 (a, b) 满足 gcd(a, b) = 1,那么称这个二元组为“幸运对”,如果 (a, b), (a + 1, b + 1), ..., (a + k, b + k),均为“幸运对”,那么称这 n + 1 个幸运对组成了一条长度为 k + 1 的“幸运链”,现在有 n 组数据,每组数据给定两个正整数 a, b,问以 (a, b) 为起点所能构成的最长的幸运链有多长,若长度可以无穷大则输出 -1。

数据范围:n <= 1e6, 1 <= a, b <= 1e7

方便起见,我们默认 a < b。

显然,长度为无穷大的情况就是 b - a = 1,长度为 0 的情况就是 gcd(a, b) != 1。下面我们讨论除这两种之外的情况。

根据“更相减损法”,gcd(a, b) = gcd(a, b - a),那么 gcd(a + k, b + k) = gcd(a + k, b - a),记 b - a 这一定值为 x。如果 gcd(a + k, x) != 1,那么此时 a + k 一定是 x 的某个质因数的倍数,计算“幸运链”的最大长度,就是要找到最小的满足这一条件的整数 k。

再进一步思考,如果 x 的某个质因数为 p,如果 a + k 为 p 的倍数,那么显然 k = p - a % p。枚举 x 所有的质因数就可以得出答案。

现在只剩最后一个问题要解决,如果每组数据都使用试除法来计算 x 的质因数,那么时间复杂度为 O(n√x),n 最大可达 1e6,x 最大可达 1e7 - 1,显然会超时。

如何解决呢?素数的线性筛法中,我们需要利用数组 low 来记录某个数的最小的质因数。程序的一开始,可以先跑一遍线性筛,预处理出 low[1] ~ low[1e7]。这样一来,对于 x,low[x] 就是它最小的质因数。不断用 low[x] 去更新答案,再令 x /= low[x],如此往复,直至 x = 1,一定不会漏掉 x 的某个质因数,而最坏情况下 x 也不过只有 log2(x) 个质因数,时间复杂度被降到了 O(nlogx),可以通过 4s 的时间限制。

代码(输入数据较多,所以我用了快读快写):

#include <bits/stdc++.h>

using namespace std;

inline void read_int(int& a) {  // 快读
  int x = 0, s = 0;
  char c = getchar();
  while (c < '0' || c > '9') { if (c == '-') ++s; c = getchar(); }
  while ('0' <= c && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
  a = (s ? -x : x);
  return;
}

inline void write_int(int a) {  // 快写
  static char buf[11];
  int p = 0;
  if (a < 0) { putchar('-'); a = -a; }
  if (!a) buf[p++] = '0';
  else { while (a) { buf[p++] = a % 10 + '0'; a /= 10; } }
  while (~--p) putchar(buf[p]);
  return;
}

int gcd(int a, int b) {
  while (b) swap(a %= b, b);
  return a;
}

int low[10000001];  // low[i] 即 i 的最小的质因数
vector<int> prime;

void solve() {
  int a, b;
  read_int(a);
  read_int(b);
  if (a > b) swap(a, b);

  if (gcd(a, b) != 1) {
    puts("0");
    return;
  }
  if (b - a == 1) {
    puts("-1");
    return;
  }

  int x = b - a;
  int ans = 0x3f3f3f3f;
  while (x != 1) {
    ans = min(ans, low[x] - a % low[x]);  // 不断用 low[x] 更新答案, 直至 x = 1
    x /= low[x];
  }
  write_int(ans);
  puts("");
}

int main() {
  for (int i = 2; i <= 10000000; ++i) {  // 线性筛
    if (low[i] == 0) {
      low[i] = i;
      prime.push_back(i);
    }
    for (size_t j = 0; j < prime.size(); ++j) {
      if (prime[j] > low[i] || prime[j] > 10000000 / i) break;
      low[i * prime[j]] = prime[j];
    }
  }

  int tt = 1;
  scanf("%d", &tt);

  while (tt--) {
    solve();
  }

  return 0;
}

 

迟到了将近 7 个月的补题ヽ(*。>Д<)o゜

 

posted @ 2022-12-18 12:50  xb2  阅读(29)  评论(0编辑  收藏  举报