【CodeForces】CodeForces Round #400 (Div. 1 + Div. 2) 题解

【比赛链接】

【题解链接】

**【A】**A Serial Killer

【思路要点】

  • 维护两个字符串模拟。
  • 时间复杂度 O ( N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
string a, b, c;
int main() {
  cin >> a >> b;
  int n; cin >> n;
  cout << a << ' ' << b << endl;
  for (int i = 1; i <= n; i++) {
      cin >> c;
      if (a == c) cin >> a;
      else cin >> b;
      cout << a << ' ' << b << endl;
  }
  return 0;
}

**【B】**Sherlock and his girlfriend

【思路要点】

  • N 2 ,将所有数涂为一种颜色。
  • 否则,将质数涂为一种颜色,非质数涂为一种颜色。
  • 时间复杂度 O ( N N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
bool prime(int x) {
  for (int i = 2; i * i <= x; i++)
      if (x % i == 0) return false;
  return true;
}
int main() {
  int n; read(n);
  if (n <= 2) {
      printf("%d\n", 1);
      for (int i = 1; i <= n; i++)
          printf("%d ", 1);
      return 0;
  }
  printf("%d\n", 2);
  for (int i = 1; i <= n; i++)
      printf("%d ", 1 + prime(i + 1));
  return 0;
}

**【C】**Molly’s Chemicals

【思路要点】

  • 枚举可能的序列和(至多 O ( L o g K ) 种)。
  • 枚举右端点,维护一个以前缀和为下标的左端点map,查询对应合法的左端点的个数即可。
  • 时间复杂度 O ( N L o g N L o g V )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, a[MAXN];
long long ans, k, s[MAXN];
long long calc(long long x) {
  static map <long long, int> mp;
  long long ans = 0; mp.clear();
  for (int i = 1; i <= n; i++) {
      mp[s[i - 1]]++;
      ans += mp[s[i] - x];
  }
  return ans;
}
int main() {
  read(n), read(k);
  for (int i = 1; i <= n; i++)
      read(a[i]), s[i] = s[i - 1] + a[i];
  long long now = 1;
  while (abs(now) <= 1e15) {
      ans += calc(now);
      now = now * k;
      if (now == 1) break;
  }
  writeln(ans);
  return 0;
}

**【D】**The Door Problem

【思路要点】

  • 考虑每一扇门,若其原本处于开启状态,其对应的两个开关应该开闭状态相同;否则其对应的两个开关应该开闭状态不同。
  • 将开关的开闭看作变量,容易得到一个2-SAT的解法。
  • 注意到本题中所建出的图为无向图,我们只需要用并查集简单维护即可。
  • 时间复杂度 O ( N + M )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 2e5 + 5;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int n, m, f[MAXN], r[MAXN], point[MAXN][2];
vector <int> p[MAXN];
int F(int x) {
  if (f[x] == x) return x;
  else return f[x] = F(f[x]);
}
int main() {
  read(n), read(m);
  for (int i = 1; i <= n; i++)
      read(r[i]);
  for (int i = 1; i <= m; i++) {
      int x; read(x);
      for (int j = 1; j <= x; j++) {
          int pos; read(pos);
          p[pos].push_back(i);
      }
  }
  int tot = 0;
  for (int i = 1; i <= m; i++) {
      point[i][0] = ++tot;
      point[i][1] = ++tot;
  }
  for (int i = 1; i <= tot; i++)
      f[i] = i;
  for (int i = 1; i <= n; i++) {
      int x = p[i][0];
      int y = p[i][1];
      if (r[i] == 0) {
          f[F(point[x][0])] = F(point[y][1]);
          f[F(point[x][1])] = F(point[y][0]);
      } else {
          f[F(point[x][0])] = F(point[y][0]);
          f[F(point[x][1])] = F(point[y][1]);
      }
  }
  for (int i = 1; i <= m; i++)
      if (F(point[i][0]) == F(point[i][1])) {
          printf("NO\n");
          return 0;
      }
  printf("YES\n");
  return 0;
}

**【E】**The Holmes Children

【思路要点】

  • 不难发现 f = ϕ , g = 1 f = i d
  • 题目本质上是对 N 求了 K + 1 2 次欧拉函数。
  • 注意到奇数求欧拉函数后会变为偶数,而偶数求欧拉函数后会减半,所以 N 在求 O ( L o g N ) 次欧拉函数后就会变成1。
  • 变成1后终止该过程即可。
  • 注意输出时对 10 9 + 7 取模。
  • 时间复杂度 O ( N L o g N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
const int P = 1e9 + 7;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
long long phi(long long x) {
  long long ans = x;
  for (int i = 2; 1ll * i * i <= x; i++) {
      if (x % i == 0) {
          ans = ans / i * (i - 1);
          while (x % i == 0) x /= i;
      }
  }
  if (x != 1) ans = ans / x * (x - 1);
  return ans;
}
int main() {
  long long n, k;
  read(n), read(k);
  if (k % 2 == 1) k = (k + 1) / 2;
  else k /= 2;
  while (k--) {
      n = phi(n);
      if (n == 1) break;
  }
  writeln(n % P);
  return 0;
}

**【F】**Sherlock’s bet to Moriarty

【思路要点】

  • 显然如果将题目中的多边形转成对偶图,那么会得到一棵树。
  • 对这棵树进行树分治,每次在分治重心上标上其在点分树上的深度即是一组符合条件的解。
  • 时间复杂度 O ( N L o g N )
  • 关于实现上的一些细节:
  • 我们当然可以使用传统的平面图转对偶图的方法来解题,但我们也可以利用本题的特殊性得到一个 编程复杂度更低的做法:对所有边按照两侧较少的点的数量排序,按少到多的顺序加入多边形,显然每加入一条边都会形成一个不可分割的多边形,也就是一个节点。维护另外一侧仍要被继续分割的多边形的节点顺序即可。
  • 关于对多边形进行标号,我们只需要对每个多边形用vector维护一个降序的顶点集合,利用vector的字典序比较即可对多边形进行排序,从而标号。注意两个多边形至多会有两个公共点,因此字典序比较的复杂度是 O ( 1 ) 的。

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 100005;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
struct edge {int x, y, val; } e[MAXN];
int n, m, tot, num[MAXN], ans[MAXN];
int root, weight[MAXN], size[MAXN];
int nxt[MAXN], home[MAXN];
bool vis[MAXN];
vector <int> a[MAXN], nodes[MAXN];
map <int, int> behind[MAXN];
bool cmp(edge x, edge y) {
  return x.val < y.val;
}
bool cnp(int x, int y) {
  return nodes[x] < nodes[y];
}
void getroot(int pos, int fa, int tot) {
  size[pos] = 1; weight[pos] = 0;
  for (unsigned i = 0; i < a[pos].size(); i++)
      if (a[pos][i] != fa && !vis[a[pos][i]]) {
          getroot(a[pos][i], pos, tot);
          size[pos] += size[a[pos][i]];
          chkmax(weight[pos], size[a[pos][i]]);
      }
  chkmax(weight[pos], tot - size[pos]);
  if (weight[pos] < weight[root]) root = pos;
}
void calcsize(int pos, int fa) {
  size[pos] = 1;
  for (unsigned i = 0; i < a[pos].size(); i++)
      if (a[pos][i] != fa && !vis[a[pos][i]]) {
          calcsize(a[pos][i], pos);
          size[pos] += size[a[pos][i]];
      }
}
void work(int pos, int depth) {
  vis[pos] = true;
  ans[num[pos]] = depth;
  calcsize(pos, 0);
  for (unsigned i = 0; i < a[pos].size(); i++)
      if (!vis[a[pos][i]]) {
          root = 0;
          getroot(a[pos][i], pos, size[a[pos][i]]);
          work(root, depth + 1);
      }
}
void debug(int pos) {
  printf("%d:\n", pos);
  printf("  Vertex:");
  for (unsigned i = 0; i < nodes[pos].size(); i++)
      printf(" %d", nodes[pos][i]);
  printf("\n");
  printf("  Edge:");
  for (unsigned i = 0; i < a[pos].size(); i++)
      printf(" %d", a[pos][i]);
  printf("\n");
  printf(" Number: %d\n", num[pos]);
}
int main() {
  read(n), read(m);
  for (int i = 1; i <= m; i++) {
      read(e[i].x), read(e[i].y);
      if (e[i].x > e[i].y) swap(e[i].x, e[i].y);
      e[i].val = e[i].y - e[i].x - 1;
      chkmin(e[i].val, n - 2 - e[i].val);
  }
  sort(e + 1, e + m + 1, cmp);
  for (int i = 1; i <= n; i++)
      if (i == n) nxt[n] = 1;
      else nxt[i] = i + 1;
  for (int i = 1; i <= m; i++) {
      int now = ++tot, s = e[i].y, t = e[i].x;
      if (e[i].y - e[i].x - 1 == e[i].val) swap(s, t);
      nodes[now].push_back(s);
      nodes[now].push_back(t);
      int p = s;
      while (nxt[s] != t) {
          int q = nxt[s];
          nodes[now].push_back(q);
          if (behind[p][q] != 0) {
              int tmp = behind[p][q];
              a[tmp].push_back(now);
              a[now].push_back(tmp);
          }
          nxt[s] = nxt[p = q];
      }
      if (behind[p][t] != 0) {
          int tmp = behind[p][t];
          a[tmp].push_back(now);
          a[now].push_back(tmp);
      }
      behind[s][t] = now;
      if (i != m) continue;
      now = ++tot; swap(s, t);
      a[now].push_back(now - 1);
      a[now - 1].push_back(now);
      nodes[now].push_back(s);
      nodes[now].push_back(t);
      p = s;
      while (nxt[s] != t) {
          int q = nxt[s];
          nodes[now].push_back(q);
          if (behind[p][q] != 0) {
              int tmp = behind[p][q];
              a[tmp].push_back(now);
              a[now].push_back(tmp);
          }
          nxt[s] = nxt[p = q];
      }
      if (behind[p][t] != 0) {
          int tmp = behind[p][t];
          a[tmp].push_back(now);
          a[now].push_back(tmp);
      }
      behind[s][t] = now;
  }
  if (m == 0) tot++;
  for (int i = 1; i <= tot; i++) {
      sort(nodes[i].begin(), nodes[i].end());
      reverse(nodes[i].begin(), nodes[i].end());
      home[i] = i;
  }
  sort(home + 1, home + tot + 1, cnp);
  for (int i = 1; i <= tot; i++)
      num[home[i]] = i;
  weight[root = 0] = n + 1;
  getroot(1, 0, n);
  work(root, 1);
  for (int i = 1; i <= tot; i++)
      printf("%d ", ans[i]);
  return 0;
}

**【G】**Sherlock and the Encrypted Data

【思路要点】

  • 我们发现数值是否减小仅和原数中出现的最大的十六进制数位有关。
  • 数位DP,记 f i , j , s 表示当前完成了最高的 i 位,已经不再紧贴上界,当前出现的最大十六进制数位为 j ,最后16位已经出现的数值为 s
  • f i , j , s 可以在 O ( 2 16 16 3 ) 的时间内通过简单数位DP求出。
  • 对于每个询问,我们只需要额外对于紧贴上界部分进行计算即可。
  • 时间复杂度 O ( 2 16 16 3 + Q 16 2 )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 17;
const int MAXS = 1 << 16;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
int a[MAXN]; char s[MAXN];
long long dp[MAXN][MAXN][MAXS];
long long work(bool flg, int pos, int Max, int s) {
  if (pos == 0) {
      return ((1 << Max) & s) != 0;
  }
  if (flg) {
      long long ans = 0;
      for (int i = 0; i <= a[pos]; i++)
          if (pos <= 4) ans += work(i == a[pos], pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4));
          else ans += work(i == a[pos], pos - 1, max(Max, i), s);
      return ans;
  } else {
      if (dp[pos][Max][s] != -1) return dp[pos][Max][s];
      long long ans = 0;
      for (int i = 0; i <= 15; i++)
          if (pos <= 4) ans += work(false, pos - 1, max(Max, i), s ^ (i << (pos - 1) * 4));
          else ans += work(false, pos - 1, max(Max, i), s);
      return dp[pos][Max][s] = ans;
  }
}
long long solve() {
  return work(true, 16, 0, 0);
}
int main() {
  int T; read(T);
  memset(dp, -1, sizeof(dp));
  while (T--) {
      scanf("%s", s + 1);
      int len = strlen(s + 1);
      memset(a, 0, sizeof(a));
      for (int i = 1; i <= len; i++) {
          int now = s[i] - '0';
          if (s[i] >= 'a') now = s[i] - 'a' + 10;
          a[len - i + 1] = now;
      }
      if (len == 1 && a[1] == 0) a[1]++;
      for (int i = 1; true; i++)
          if (a[i]) {
              a[i]--;
              break;
          } else a[i] = 15;
      long long ans = -solve();
      scanf("%s", s + 1);
      len = strlen(s + 1);
      memset(a, 0, sizeof(a));
      for (int i = 1; i <= len; i++) {
          int now = s[i] - '0';
          if (s[i] >= 'a') now = s[i] - 'a' + 10;
          a[len - i + 1] = now;
      }
      ans += solve();
      writeln(ans);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/81365325
今日推荐