【CodeForces】AIM Tech Round 5 (Div. 1 + Div. 2) 题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82184245

【比赛链接】

【题解链接】

**【A】**Find Square

【思路要点】

  • 答案即为所有黑色方格坐标的平均值。
  • 时间复杂度 O ( N M )

【代码】


#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, m, sx, sy, cnt;
int main() {
  read(n), read(m);
  for (int i = 1; i <= n; i++) {
      static char s[MAXN];
      scanf("\n%s", s + 1);
      for (int j = 1; j <= m; j++)
          if (s[j] == 'B') {
              cnt++;
              sx += i;
              sy += j;
          }
  }
  printf("%d %d\n", sx / cnt, sy / cnt);
  return 0;
}

**【B】**Unnatural Conditions

【思路要点】

  • 我们可以构造两个和为 10 1000 的数,使得它们能够回答任何输入。
  • A = 44444...445 , B = 55555...555 即可。
  • 时间复杂度 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("");
}
int main() {
  string a, b;
  for (int i = 1; i <= 1000; i++) {
      a += '4';
      b += '5';
  }
  a += '5', b += '5';
  cout << a << endl;
  cout << b << endl;
  return 0;
}

**【C】**Rectangles

【思路要点】

  • 用前/后缀和计算矩形的交,就可以在 O ( N ) 的时间内得出除去任何一个矩形后其余矩形的交。
  • 枚举去掉的矩形,若剩余矩形的交非空,输出其中任意一点。
  • 时间复杂度 O ( N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200005;
const int INF = 1e9;
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 point {int x, y; };
point min(point a, point b) {return (point) {min(a.x, b.x), min(a.y, b.y)}; }
point max(point a, point b) {return (point) {max(a.x, b.x), max(a.y, b.y)}; }
point l[MAXN], r[MAXN], prel[MAXN], prer[MAXN], sufl[MAXN], sufr[MAXN];
int n;
int main() {
  read(n);
  for (int i = 1; i <= n; i++) {
      read(l[i].x), read(l[i].y);
      read(r[i].x), read(r[i].y);
  }
  prel[0] = sufl[n + 1] = (point) {-INF, -INF};
  prer[0] = sufr[n + 1] = (point) {INF, INF};
  for (int i = 1; i <= n; i++) {
      prel[i] = max(prel[i - 1], l[i]);
      prer[i] = min(prer[i - 1], r[i]);
  }
  for (int i = n; i >= 1; i--) {
      sufl[i] = max(sufl[i + 1], l[i]);
      sufr[i] = min(sufr[i + 1], r[i]);
  }
  for (int i = 1; i <= n; i++) {
      point tl = max(prel[i - 1], sufl[i + 1]);
      point tr = min(prer[i - 1], sufr[i + 1]);
      if (tl.x <= tr.x && tl.y <= tr.y) {
          printf("%d %d\n", tl.x, tl.y);
          return 0;
      }
  }
  return 0;
}

**【D】**Order book

【思路要点】

  • 考虑一个 A c c e p t 操作带来的影响,被 A c c e p t o f f e r (令其价格为 x )的类型不能由此次操作确定,但当时存在的价格大于 x o f f e r 必须为 s e l l ,当时存在的价格小于 x o f f e r 必须为 b u y
  • 在最后一个 A c c e p t 操作前的 A d d 操作加进的 o f f e r 在上述过程考虑结束后若必须为 s e l l / b u y ,则方案数不变;若同时必须是 s e l l b u y ,则不存在合法方案;否则,它既可以是 s e l l ,也可以是 b u y ,方案数应当乘以 2 (注意此时这个 o f f e r 一定被 A c c e p t 了)。
  • 在最后一个 A c c e p t 操作后的 A d d 操作需要单独考虑,若一个 o f f e r 被加入时已经小于某一个 b u y o f f e r ,或大于某一个 s e l l o f f e r ,那么它的类型时确定的,否则,它的类型是不确定的。记不确定的 o f f e r 个数为 c n t ,答案应当再乘以 c n t + 1
  • s t d :: p r i o r i t y _ q u e u e 来模拟上述过程,时间复杂度 O ( N L o g N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 400005;
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("");
}
bool type[MAXN]; int x[MAXN];
map <int, bool> black, white, exist;
priority_queue <int> hb, hw;
int main() {
  int n; read(n);
  int last = 0;
  for (int i = 1; i <= n; i++) {
      char opt[15];
      scanf("\n%s%d", opt + 1, &x[i]);
      type[i] = opt[2] == 'D';
      if (!type[i]) last = i, exist[x[i]] = true;
  }
  for (int i = 1; i <= n; i++)
      if (type[i]) {
          hb.push(-x[i]);
          hw.push(x[i]);
      } else {
          while (!hb.empty() && hb.top() >= -x[i]) {
              if (hb.top() != -x[i]) black[-hb.top()] = true;
              hb.pop();
          }
          while (!hw.empty() && hw.top() >= x[i]) {
              if (hw.top() != x[i]) white[hw.top()] = true;
              hw.pop();
          }
      }
  int ans = 1, Max = 0, Min = 1e9;
  for (int i = 1; i <= last; i++)
      if (type[i]) {
          int tmp = 0;
          tmp += !black[x[i]];
          if (black[x[i]] && !exist[x[i]]) chkmax(Max, x[i]);
          tmp += !white[x[i]];
          if (white[x[i]] && !exist[x[i]]) chkmin(Min, x[i]);
          ans = ans * tmp % P;
      }
  int cnt = 1;
  for (int i = last + 1; i <= n; i++)
      if (x[i] >= Max && x[i] <= Min) cnt++;
  writeln(1ll * ans * cnt % P);
  return 0;
}

**【E】**Restore Array

【思路要点】

  • 我们首先考虑所有数都相等的情况。
  • 若所有数都等于 0 ,那么任意一组所有数都相同的解都可以作为答案。
  • 若所有数都相等,且不为 0 ,那么问题无解。
  • 接下来我们认为至少存在两个数不等。
  • 注意到 ( a + b ) % b = a ( b > a )
  • M a x = m a x { b i } ,取 b i 使得 b i = M a x , b i 1 M a x ,令 a i = b i
  • b i 1 = 0 ,令 a i 1 = 2 a i ,否则令 a i 1 = a i + b i 1
  • 对于剩余所有 j ( j i , i 1 ) ,令 a j = a j + 1 + b j 即可。
  • 时间复杂度 O ( N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200005;
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, Max, a[MAXN];
long long ans[MAXN];
int main() {
  read(n);
  for (int i = 1; i <= n; i++)
      read(a[i]), chkmax(Max, a[i]);
  if (Max == 0) {
      printf("YES\n");
      for (int i = 1; i <= n; i++)
          printf("%d ", 1);
      printf("\n");
      return 0;
  }
  a[0] = a[n];
  for (int i = 1; i <= n; i++)
      if (a[i] == Max && a[i - 1] != Max) {
          long long now = a[i];
          int pos = i - 1; ans[i] = now;
          if (pos == 0) pos = n;
          while (pos != i) {
              if (a[pos] == 0 && now == a[i]) now += a[i];
              else now += a[pos];
              ans[pos] = now;
              if (--pos == 0) pos = n;
          }
          printf("YES\n");
          for (int i = 1; i <= n; i++)
              write(ans[i]), putchar(' ');
          printf("\n");
          return 0;
      }
  printf("NO\n");
  return 0;
}

**【F】**Make Symmetrical

【思路要点】

  • 两个关于过原点的某条直线对称的点到原点距离相等。
  • 而不定方程 x 2 + y 2 = C ( C 10 12 ) 的非负整数解的个数至多有 M = 144 个。
  • 对每个点按照到原点的距离分类,在插入或删除时暴力维护即可。
  • 时间复杂度 O ( Q M 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("");
}
struct point {int x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point operator * (point a, int b) {return (point) {a.x * b, a.y * b}; }
point operator / (point a, int b) {return (point) {a.x / b, a.y / b}; }
long long moo(point a) {return a.x * a.x + a.y * a.y; }
bool operator < (point a, point b) {
  if (a.x == b.x) return a.y < b.y;
  else return a.x < b.x;
}
int gcd(int x, int y) {
  if (y == 0) return x;
  else return gcd(y, x % y);
}
int n, tot, cnt;
map <point, int> ans;
map <long long, int> mp;
vector <point> a[MAXN];
int main() {
  read(n);
  for (int i = 1; i <= n; i++) {
      int opt; point tmp;
      read(opt), read(tmp.x), read(tmp.y);
      if (opt == 1) {
          cnt++;
          long long tnp = moo(tmp);
          if (mp[tnp] == 0) mp[tnp] = ++tot;
          int pos = mp[tnp];
          ans[tmp / gcd(tmp.x, tmp.y)]++;
          for (unsigned j = 0; j < a[pos].size(); j++) {
              point sum = tmp + a[pos][j];
              sum = sum / gcd(sum.x, sum.y);
              ans[sum] += 2;
          }
          a[pos].push_back(tmp);
      }
      if (opt == 2) {
          cnt--;
          long long tnp = moo(tmp);
          if (mp[tnp] == 0) mp[tnp] = ++tot;
          int pos = mp[tnp];
          ans[tmp / gcd(tmp.x, tmp.y)]--;
          for (unsigned j = 0; j < a[pos].size(); j++)
              if (tmp.x == a[pos][j].x && tmp.y == a[pos][j].y) {
                  a[pos].erase(a[pos].begin() + j);
                  break;
              }
          for (unsigned j = 0; j < a[pos].size(); j++) {
              point sum = tmp + a[pos][j];
              sum = sum / gcd(sum.x, sum.y);
              ans[sum] -= 2;
          }
      }
      if (opt == 3) writeln(cnt - ans[tmp / gcd(tmp.x, tmp.y)]);
  }
  return 0;
}

**【G】**Guess the number

【思路要点】

  • d p i , j 表示剩余 i 次询问的机会,当前确定了 k j 的情况下,能够确定的最大长度。

  • 设计出状态之后,是容易转移的:

    d p i , j = d p i 1 , j + f ( i 1 , i , j + 1 + d p i 1 , j )

    f ( k , i , j ) = { 0 i = 0 1 + d p k , j + f ( k , i 1 , j + 1 + d p k , j ) i 0

  • 并且注意到当 j 10000 d p i , j = d p i , 10000 ,因此我们可以利用该性质对上述过程进行剪枝。

  • d p 5 , 1 恰好为 10004205361450474 ,说明题目给出的限制实际上是最紧的。

  • 利用 d p 的结果进行交互即可。

  • 时间复杂度 O ( 5 10000 2 ) ,实际运行复杂度很不满。

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 10005;
const int MAXM = 10;
const long long INF = 1e18;
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 dp[MAXM][MAXN], a[MAXN];
long long times(long long a, long long b) {
  if ((long double) a * b >= INF) return INF;
  else return a * b;
}
int main() {
  for (int i = 1; i <= 5; i++)
  for (int j = 1; j <= 10000; j++) {
      long long ans = -1, pos = j;
      for (int k = 0; k <= j; k++) {
          ans += dp[i - 1][pos] + 1;
          chkmin(ans, INF);
          pos += dp[i - 1][pos] + 1;
          if (pos >= 10000) {
              ans += times(dp[i - 1][10000] + 1, j - k);
              chkmin(ans, INF);
              break;
          }
      }
      dp[i][j] = ans;
  }
  int cnt = 5; long long pos = 1;
  while (true) {
      int tmp = min(pos, 10000ll), tnp = tmp;
      cout << tmp; a[0] = pos - 1;
      for (int i = 1; i <= tmp; i++) {
          pos += dp[cnt - 1][tnp];
          tnp = min(tnp + dp[cnt - 1][tnp] + 1, 10000ll);
          a[i] = pos; pos++;
          cout << ' ' << a[i];
      }
      cout << endl;
      read(tnp);
      if (tnp == -1) return 0;
      cnt--; pos = a[tnp] + 1;
  }
  return 0;
}

**【H】**Make Square

【思路要点】

  • 首先我们可以将所有数的完全平方因子除去,只剩下每个质因数至多一种。
  • 由此我们也可以看出,两种操作实际上是本质相同的,考虑只使用除法操作。
  • 将两个数 A , B 的乘积修改为完全平方数的过程可以看做对 A , B 各进行若干次除法操作,使得它们等于同一个数 C
  • 考虑离线询问,从左到右枚举询问的右端点 i ,并维护数组 M a x C , c n t ,表示在前 i 1 个数中,进行 c n t 次除法操作可以得到 C 的最靠右的数的位置;再维护数组 r c n t 表示使得答案为 c n t 的最靠右的左端点,显然有了 r 数组后是容易回答询问的。
  • 对于每一个右端点 i ,考虑 r 数组的变化,枚举 A i 的因数 C ,再枚举与 A i 配为完全平方数的 B 得到 C 的操作次数,即可借助 M a x 数组更新 r 数组,对于 M a x 数组的更新类似。
  • 时间复杂度 O ( N A i M a x A n s + Q M a x A n s ) ,其中 M a x A n s = 11

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 2e5 + 5;
const int MAXQ = 2e6 + 5;
const int MAXV = 6e6 + 5;
const int Maxans = 12;
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, q, a[MAXN], ans[MAXQ];
int r[Maxans + 5], Max[MAXV][Maxans + 5];
int tot, prime[MAXV], f[MAXV], cnt[MAXV];
vector <int> pos[MAXN], home[MAXN];
void init() {
  for (int i = 2; i < MAXV; i++) {
      if (f[i] == 0) prime[++tot] = f[i] = i, cnt[i] = 1;
      for (unsigned j = 1; j <= tot && prime[j] <= f[i]; j++) {
          int tmp = prime[j] * i;
          if (tmp >= MAXV) break;
          f[tmp] = prime[j];
          cnt[tmp] = cnt[i] + 1;
      }
  }
}
int process(int x) {
  int ans = 1;
  for (int i = 1; prime[i] * prime[i] <= x; i++)
      if (x % prime[i] == 0) {
          int cnt = 0;
          while (x % prime[i] == 0) x /= prime[i], cnt++;
          if (cnt & 1) ans *= prime[i];
      }
  return ans * x;
}
int main() {
  read(n), read(q);
  init();
  for (int i = 1; i <= n; i++) {
      read(a[i]);
      a[i] = process(a[i]);
  }
  for (int i = 1; i <= q; i++) {
      int x, y; read(x), read(y);
      pos[y].push_back(x);
      home[y].push_back(i);
  }
  for (int i = 1; i <= n; i++) {
      for (int j = 1; j * j <= a[i]; j++) {
          if (a[i] % j == 0) {
              int now = cnt[a[i] / j];
              for (int k = 0; k + now <= Maxans; k++)
                  chkmax(r[k + now], Max[j][k]);
              now = cnt[j];
              for (int k = 0; k + now <= Maxans; k++)
                  chkmax(r[k + now], Max[a[i] / j][k]);
          }
      }
      for (int j = 1; j * j <= a[i]; j++) {
          if (a[i] % j == 0) {
              chkmax(Max[j][cnt[a[i] / j]], i);
              chkmax(Max[a[i] / j][cnt[j]], i);
          }
      }
      for (unsigned j = 0; j < pos[i].size(); j++) {
          int tans = Maxans;
          for (int k = 0; k <= Maxans; k++)
              if (r[k] >= pos[i][j]) chkmin(tans, k);
          ans[home[i][j]] = tans;
      }
  }
  for (int i = 1; i <= q; i++)
      writeln(ans[i]);
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/82184245