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

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

【比赛链接】

【题解链接】

**【A】**The Rank

【思路要点】

  • 按照题意模拟。
  • 时间复杂度 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 getv() {
  int ans = 0, x;
  read(x), ans += x;
  read(x), ans += x;
  read(x), ans += x;
  read(x), ans += x;
  return ans;
}
int main() {
  int n, ans = 1;
  read(n); n--;
  int now = getv();
  while (n--) {
      int tmp = getv();
      if (tmp > now) ans++;
  }
  writeln(ans);
  return 0;
}

**【B】**The Bits

【思路要点】

  • 令被交换的两个位置为 i j
  • 显然若要使得结果发生变化,必须要有 A i A j
  • 并且 B i , B j 中至少有一个不为 1
  • 那么 A n s = w a y s ( A i A j ) w a y s ( A i A j   & &   B i = B j = 1 )
  • 时间复杂度 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("");
}
char s[MAXN], t[MAXN];
long long one, zero, oneone, zeroone;
int main() {
  int n; read(n);
  scanf("\n%s", s + 1);
  scanf("\n%s", t + 1);
  for (int i = 1; i <= n; i++) {
      if (s[i] == '1') {
          one++;
          if (t[i] == '1') oneone++;
      } else {
          zero++;
          if (t[i] == '1') zeroone++;
      }
  }
  writeln(one * zero - oneone * zeroone);
  return 0;
}

**【C】**The Phone Number

【思路要点】

  • 考虑枚举 L D S 的长度 X ,我们可以通过如下方式得到一个 L I S 长度为 N X 的序列:

    1 , 2 , 3 , . . . , N 分成 N X 段,每段长度不超过 X ,将每一段数字翻转。

  • 枚举找到使得 X + N X 最小的 X 即可。

  • 时间复杂度 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 a[MAXN];
int main() {
  int n; read(n);
  int ans = 1e9, x = 0;
  for (int i = 1; i <= n; i++) {
      int y = n / i + (n % i != 0);
      if (i + y < ans) {
          ans = i + y;
          x = i;
      }
      a[i] = i;
  }
  for (int i = 1; i <= n; i += x) {
      int j = min(i + x - 1, n);
      reverse(a + i, a + j + 1);
  }
  for (int i = 1; i <= n; i++)
      printf("%d ", a[i]);
  return 0;
}

**【D】**The Wu

【思路要点】

  • 注意到 2 N K 均很小,预处理出所有可能的询问的答案,询问时 O ( 1 ) 回答即可。
  • 时间复杂度 O ( 2 2 N + 2 N K + Q )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 15;
const int MAXK = 105;
const int MAXS = 5005;
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, m, goal, w[MAXN], bit[MAXN];
int val[MAXS], cnt[MAXS], ans[MAXS][MAXK];
int calc(int x, int y) {
  int tmp = goal ^ (x ^ y);
  return val[tmp];
}
int getin() {
  static char s[MAXN];
  scanf("\n%s", s + 1);
  int ans = 0;
  for (int i = 1; i <= n; i++)
      if (s[i] == '1') ans += bit[i];
  return ans;
}
int main() {
  read(n), read(m), read(q);
  for (int i = 1; i <= n; i++) {
      bit[i] = 1 << (i - 1);
      read(w[i]);
  }
  goal = (1 << n) - 1;
  for (int s = 0; s <= goal; s++) {
      val[s] = 0;
      for (int i = 1; i <= n; i++)
          if (bit[i] & s) val[s] += w[i];
  }
  for (int i = 1; i <= m; i++) {
      int x = getin();
      cnt[x]++;
  }
  for (int s = 0; s <= goal; s++) {
      for (int t = 0; t <= goal; t++)
          if (calc(s, t) <= 100) ans[s][calc(s, t)] += cnt[t];
      for (int i = 1; i <= 100; i++)
          ans[s][i] += ans[s][i - 1];
  }
  while (q--) {
      int s = getin(), k;
      read(k);
      writeln(ans[s][k]);
  }
  return 0;
}

**【E】**The Supersonic Rocket

【思路要点】

  • 问题实际上要我们判断两个点集的凸包能否经过平移和旋转后重合。

  • 令两个点集的凸包为 S , T ,若 | S | | T | ,显然不可能重合。

  • 对于每一个凸包(不妨为 S ),取几何中心 M i d (或凸包顶点坐标的平均值)

    构造这样一个序列:

    S e q S = d i s t ( M i d , S 1 ) , d i s t ( S 1 , S 2 ) , d i s t ( M i d , S 2 ) , d i s t ( S 2 , S 3 ) , . . . , d i s t ( M i d , S | S | ) , d i s t ( S | S | , S 1 )

  • 2 S e q S 表示 S e q S 倍长后的序列,那么两个凸包同构当且仅当 S e q T 2 S e q S 中出现过。

  • K M P 算法判断即可。

  • 时间复杂度 O ( N L o g N + M L o g M )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 4e5 + 5;
const long double eps = 1e-9;
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}; }
long long operator * (point a, point b) {return 1ll * a.x * b.y - 1ll * a.y * b.x; }
bool operator < (point a, point b) {
  if (a.y == b.y) return a.x < b.x;
  else return a.y < b.y;
}
long long dist(point a) {return 1ll * a.x * a.x + 1ll * a.y * a.y; }
int n, m, q;
point a[MAXN], b[MAXN], fp;
long double val[MAXN], vbl[MAXN];
bool solve() {
  if (n != m) return false;
  n--, m--;
  long double px = 0, py = 0;
  for (int i = 1; i <= n; i++) {
      px += a[i].x;
      py += a[i].y;
  }
  px /= n, py /= n;
  for (int i = 1; i <= n; i++) {
      val[2 * i - 1] = sqrt((px - a[i].x) * (px - a[i].x) + (py - a[i].y) * (py - a[i].y));
      val[2 * i] = sqrt(1ll * (a[i + 1].x - a[i].x) * (a[i + 1].x - a[i].x) + 1ll * (a[i + 1].y - a[i].y) * (a[i + 1].y - a[i].y));
  }
  for (int i = 1; i <= 2 * n; i++)
      val[i + 2 * n] = val[i];
  px = 0, py = 0;
  for (int i = 1; i <= m; i++) {
      px += b[i].x;
      py += b[i].y;
  }
  px /= m, py /= m;
  for (int i = 1; i <= m; i++) {
      vbl[2 * i - 1] = sqrt((px - b[i].x) * (px - b[i].x) + (py - b[i].y) * (py - b[i].y));
      vbl[2 * i] = sqrt(1ll * (b[i + 1].x - b[i].x) * (b[i + 1].x - b[i].x) + 1ll * (b[i + 1].y - b[i].y) * (b[i + 1].y - b[i].y));
  }
  int la = n * 4;
  int lb = m * 2;
  static int nxt[MAXN];
  for (int i = 2; i <= lb; i++) {
      int p = nxt[i - 1];
      while (p && fabs(vbl[p + 1] - vbl[i]) > eps)
          p = nxt[p];
      if (fabs(vbl[p + 1] - vbl[i]) <= eps) p++;
      nxt[i] = p;
  }
  int pos = 0, ans = 0;
  for (int i = 1; i <= la; i++) {
      if (fabs(val[i] - vbl[pos + 1]) <= eps) {
          pos++;
          if (pos == lb) {
              ans++;
              pos = nxt[lb];
          }
      } else {
          while (pos && fabs(val[i] - vbl[pos + 1]) > eps)
              pos = nxt[pos];
          if (fabs(val[i] - vbl[pos + 1]) <= eps) pos++;
      }
  }
  return ans != 0;
}
bool cmp(point a, point b) {
  long long tmp = (a - fp) * (b - fp);
  if (tmp == 0) return dist(a - fp) < dist(b - fp);
  else return tmp > 0;
}
bool online(point *a, int n) {
  point tmp = a[n] - a[1];
  for (int i = 2; i <= n - 1; i++)
      if (tmp * (a[i] - a[1]) != 0) return false;
  return true;
}
int main() {
  read(n), read(m);
  for (int i = 1; i <= n; i++)
      read(a[i].x), read(a[i].y);
  for (int i = 2; i <= n; i++)
      if (a[i] < a[1]) swap(a[i], a[1]);
  fp = a[1];
  sort(a + 2, a + n + 1, cmp);
  for (int i = 1; i <= m; i++)
      read(b[i].x), read(b[i].y);
  for (int i = 2; i <= m; i++)
      if (b[i] < b[1]) swap(b[i], b[1]);
  fp = b[1];
  sort(b + 2, b + m + 1, cmp);
  if (online(a, n) && online(b, m)) {
      if (dist(a[n] - a[1]) == dist(b[m] - b[1])) printf("YES\n");
      else printf("NO\n");
      return 0;
  }
  a[++n] = a[1];
  int top = 1;
  for (int i = 2; i <= n; i++) {
      while (top >= 2 && (a[top] - a[top - 1]) * (a[i] - a[top - 1]) <= 0) top--;
      a[++top] = a[i];
  }
  n = top;
  b[++m] = b[1]; top = 1;
  for (int i = 2; i <= m; i++) {
      while (top >= 2 && (b[top] - b[top - 1]) * (b[i] - b[top - 1]) <= 0) top--;
      b[++top] = b[i];
  }
  m = top;
  if (solve()) printf("YES\n");
  else printf("NO\n");
  return 0;
}

**【F】**The Neutral Zone

【思路要点】

  • 显然 A n s = p   i s   a   p r i m e f ( p ) ( N p + N p 2 + N p 3 + . . . )
  • 对于小于等于 N p ,我们直接采用上式计算。
  • 我们剩余的工作是计算 p > N , p   i s   a   p r i m e f ( p ) N p
  • N p 进行整数分块,用 M i n 25 筛计算每一块的质数 k ( k = 0 , 1 , 2 , 3 ) 次幂和即可。
  • 时间复杂度 O ( N 3 4 L o g N )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 1e5 + 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("");
}
unsigned n, A, B, C, D, limit;
unsigned f[MAXN], tot, prime[MAXN], sum1[MAXN], sum2[MAXN], sum3[MAXN];
unsigned m, x[MAXN], id1[MAXN], id2[MAXN], ans0[MAXN], ans1[MAXN], ans2[MAXN], ans3[MAXN];
void init(unsigned n) {
  for (unsigned i = 2; i <= n; i++) {
      if (f[i] == 0) {
          f[i] = i;
          prime[++tot] = i;
          sum1[tot] = sum1[tot - 1] + i;
          sum2[tot] = sum2[tot - 1] + i * i;
          sum3[tot] = sum3[tot - 1] + i * i * i;
      }
      for (unsigned j = 1; j <= tot && prime[j] <= f[i]; j++){
          unsigned tmp = prime[j] * i;
          if (tmp > n) break;
          f[tmp] = prime[j];
      }
  }
}
unsigned get2(unsigned x) {
  unsigned a = x;
  unsigned b = x + 1;
  unsigned c = 2 * x + 1;
  if (a % 2 == 0) a /= 2;
  else if (b % 2 == 0) b /= 2;
  else c /= 2;
  if (a % 3 == 0) a /= 3;
  else if (b % 3 == 0) b /= 3;
  else c /= 3;
  return a * b * c;
}
unsigned get3(unsigned x) {
  unsigned a = x;
  unsigned b = x + 1;
  if (a % 2 == 0) a /= 2;
  else b /= 2;
  return a * b * a * b;
}
int main(){
  read(n), read(A), read(B), read(C), read(D);
  limit = sqrt(n); init(limit);
  for (unsigned i = 1, j; i <= n; i = j + 1){
      j = n / (n / i); x[++m] = n / i;
      if (x[m] <= limit) id1[x[m]] = m;
      else id2[n / x[m]] = m;
      ans0[m] = x[m] - 1;
      if ((x[m] + 2) % 2 == 0) ans1[m] = ((x[m] + 2) / 2) * (x[m] - 1);
      else ans1[m] = (x[m] + 2) * ((x[m] - 1) / 2);
      ans2[m] = get2(x[m]) - 1;
      ans3[m] = get3(x[m]) - 1;
  }
  for (unsigned j = 1; j <= tot; j++)
      for (unsigned i = 1; i <= m && prime[j] * prime[j] <= x[i]; i++) {
          unsigned k = (x[i] / prime[j] <= limit) ? id1[x[i] / prime[j]] : id2[n / (x[i] / prime[j])];
          ans3[i] = ans3[i] - prime[j] * prime[j] * prime[j] * (ans3[k] - sum3[j - 1]);
          ans2[i] = ans2[i] - prime[j] * prime[j] * (ans2[k] - sum2[j - 1]);
          ans1[i] = ans1[i] - prime[j] * (ans1[k] - sum1[j - 1]);
          ans0[i] = ans0[i] - ans0[k] + j - 1;
      }
  unsigned ans = 0;
  for (int i = 1; i <= tot; i++) {
      unsigned cnt = 0;
      unsigned tmp = n;
      while (tmp >= prime[i]) {
          cnt += tmp / prime[i];
          tmp /= prime[i];
      }
      ans += cnt * (A * prime[i] * prime[i] * prime[i] + B * prime[i] * prime[i] + C * prime[i] + D);
  }
  for (unsigned i = 1; x[i] > limit; i++) {
      ans += i * (ans3[i] - ans3[i + 1]) * A;
      ans += i * (ans2[i] - ans2[i + 1]) * B;
      ans += i * (ans1[i] - ans1[i + 1]) * C;
      ans += i * (ans0[i] - ans0[i + 1]) * D;
  }
  writeln(ans);
  return 0;
}

**【G】**The Tree

【思路要点】

  • f ( i ) 表示作用在节点 i 上的 1 号操作的次数, g ( i ) 表示直接作用在节点 i 上的 1 号操作的次数,有 f ( i ) = m a x { f ( f a t h e r i ) 1 , 0 } + g ( i ) = m a x { f ( f a t h e r i ) 1 + g ( i ) , g ( i ) }

  • 对于 3 号操作,求出 f ( i ) 的值,若 f ( i ) > 0 ,点 i 为黑,否则为白。

  • 我们发现每一个点 i f ( i ) 可以写成一个关于 f ( f a t h e r i ) 的形如 f ( x ) = m a x { x + a , b } 的函数,并且令 g ( x ) = m a x { x + c , d }

    g ( f ( x ) ) = m a x { m a x { x + a , b } + c , d } = m a x { x + a + c , m a x { b + c , b } }

    即这样的函数的作用效果是可以合并的。

  • 我们用树链剖分+线段树来维护这些函数,线段树上每一个节点代表了子树内所有函数依次作用的效果总和,这样就能够在 O ( L o g 2 N ) 的时间内查询根节点到某一节点的函数依次作用的效果总和。

  • 对于 1 号操作,将节点 i a , b 均加一。

  • 对于 2 号操作,先求出 f ( i ) ,将节点 i a , b 均减去 f ( i ) ,以去除 i 及其祖先对于 i 子树内的影响,然后通过线段树的区间操作将 i 子树内除节点 i 以外的其余点的 a , b 还原为 ( 1 , 0 ) ,以移除子树内的操作效果。

  • 时间复杂度 O ( N + Q L o g 2 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("");
}
struct info {int a, b; };
info operator + (info x, info y) {
  x.a += y.a;
  x.b += y.a;
  chkmax(x.b, y.b);
  return x;
}
struct SegmentTree {
  struct Node {
      int lc, rc;
      info ans;
      bool tag;
  } a[MAXN * 2];
  int n, root, size;
  void update(int root) {
      a[root].ans = a[a[root].lc].ans + a[a[root].rc].ans;
  }
  void build(int &root, int l, int r) {
      root = ++size;
      if (l == r) {
          a[root].ans = (info) {-1, 0};
          return;
      }
      int mid = (l + r) / 2;
      build(a[root].lc, l, mid);
      build(a[root].rc, mid + 1, r);
      update(root);
  }
  void init(int x) {
      n = x;
      root = size = 0;
      build(root, 1, n);
  }
  void pushdown(int root, int l, int r) {
      if (a[root].tag) {
          int mid = (l + r) / 2;
          a[a[root].lc].ans = (info) {-(mid - l + 1), 0};
          a[a[root].lc].tag = true;
          a[a[root].rc].ans = (info) {-(r - mid), 0};
          a[a[root].rc].tag = true;
          a[root].tag = false;
      }
  }
  void inc(int root, int l, int r, int pos) {
      if (l == r) {
          a[root].ans.a++;
          a[root].ans.b++;
          return;
      }
      pushdown(root, l, r);
      int mid = (l + r) / 2;
      if (mid >= pos) inc(a[root].lc, l, mid, pos);
      else inc(a[root].rc, mid + 1, r, pos);
      update(root);
  }
  void inc(int pos) {
      inc(root, 1, n, pos);
  }
  void dec(int root, int l, int r, int pos, int delta) {
      if (l == r) {
          a[root].ans.a -= delta;
          a[root].ans.b -= delta;
          return;
      }
      pushdown(root, l, r);
      int mid = (l + r) / 2;
      if (mid >= pos) dec(a[root].lc, l, mid, pos, delta);
      else dec(a[root].rc, mid + 1, r, pos, delta);
      update(root);
  }
  void dec(int pos, int delta) {
      dec(root, 1, n, pos, delta);
  }
  info query(int root, int l, int r, int ql, int qr) {
      if (l == ql && r == qr) return a[root].ans;
      pushdown(root, l, r);
      int mid = (l + r) / 2;
      if (mid >= qr) return query(a[root].lc, l, mid, ql, qr);
      else if (mid + 1 <= ql) return query(a[root].rc, mid + 1, r, ql, qr);
      else return query(a[root].lc, l, mid, ql, mid) + query(a[root].rc, mid + 1, r, mid + 1, qr);
  }
  info query(int l, int r) {
      return query(root, 1, n, l, r);
  }
  void clear(int root, int l, int r, int ql, int qr) {
      if (l == ql && r == qr) {
          a[root].tag = true;
          a[root].ans = (info) {-(r - l + 1), 0};
          return;
      }
      pushdown(root, l, r);
      int mid = (l + r) / 2;
      if (mid >= ql) clear(a[root].lc, l, mid, ql, min(qr, mid));
      if (mid + 1 <= qr) clear(a[root].rc, mid + 1, r, max(mid + 1, ql), qr);
      update(root);
  }
  void clear(int l, int r) {
      clear(root, 1, n, l, r);
  }
} ST;
int n, m;
int size[MAXN], depth[MAXN];
int timer, dfn[MAXN], rit[MAXN];
int son[MAXN], up[MAXN], father[MAXN];
vector <int> a[MAXN];
void dfs(int pos) {
  size[pos] = 1, son[pos] = 0;
  for (unsigned i = 0; i < a[pos].size(); i++) {
      int tmp = a[pos][i];
      depth[tmp] = depth[pos] + 1;
      father[tmp] = pos;
      dfs(tmp);
      size[pos] += size[tmp];
      if (size[tmp] > size[son[pos]]) son[pos] = tmp;
  }
}
void efs(int pos, int from) {
  dfn[pos] = ++timer;
  up[pos] = from;
  if (son[pos]) efs(son[pos], from);
  for (unsigned i = 0; i < a[pos].size(); i++)
      if (a[pos][i] != son[pos]) efs(a[pos][i], a[pos][i]);
  rit[pos] = timer;
}
int query(int pos) {
  info ans = ST.query(dfn[up[pos]], dfn[pos]);
  pos = father[up[pos]];
  while (pos != 0) {
      ans = ST.query(dfn[up[pos]], dfn[pos]) + ans;
      pos = father[up[pos]];
  }
  return max(ans.a, ans.b);
}
void modify(int pos) {
  ST.dec(dfn[pos], query(pos));
  if (dfn[pos] != rit[pos]) ST.clear(dfn[pos] + 1, rit[pos]);
}
int main() {
  read(n), read(m);
  for (int i = 2; i <= n; i++) {
      int x; read(x);
      a[x].push_back(i);
  }
  dfs(1);
  efs(1, 1);
  ST.init(n);
  for (int i = 1; i <= m; i++) {
      int type, pos;
      read(type), read(pos);
      if (type == 1) ST.inc(dfn[pos]);
      if (type == 2) modify(pos);
      if (type == 3) {
          int tmp = query(pos);
          if (tmp != 0) printf("black\n");
          else printf("white\n");
      }
  }
  return 0;
}

**【H】**The Films

【思路要点】

  • 不难发现答案为 ( N + k M l e n ) n l e n _ i = 1 M ( A i + k ) B i _

    其中 l e n = r l + 1 A i 表示 i 在序列中出现的次数, B i 表示 i 在给定区间中出现的次数。

  • 对于每个不同的 k ,分别用莫队算法计算答案即可。

  • 当序列长度为 N ,询问个数为 Q ,分块大小为 B ,莫队算法的时间复杂度为 O ( N 2 B + Q B )

    B = O ( N Q ) 可以得到 O ( N Q ) 的时间复杂度。

  • K k 的不同取值的个数,上述算法复杂度瓶颈在于对于每个取值,询问个数相差较小的情况,即最坏时间复杂度为 O ( K N Q K ) = O ( N Q K )

  • 时间复杂度 O ( N K + N Q K )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 200005;
const int P = 998244353;
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 querys {int l, r, k, home; } qry[MAXN];
int prod[MAXN], inv[MAXN];
int n, m, q, size, blocks, block[MAXN];
int now, nowk, a[MAXN], b[MAXN], e[MAXN], ans[MAXN];
bool cmp(querys a, querys b) {
  return a.k < b.k;
}
bool cnp(querys a, querys b) {
  if (block[a.l] == block[b.l]) return a.r < b.r;
  else return block[a.l] < block[b.l];
}
int power(int x, int y) {
  if (y == 0) return 1;
  int tmp = power(x, y / 2);
  if (y % 2 == 0) return 1ll * tmp * tmp % P;
  else return 1ll * tmp * tmp % P * x % P;
}
void add(int col) {
  b[col]++;
  int tmp = a[col] + nowk - b[col] + 1;
  now = 1ll * now * tmp % P;
}
void del(int col) {
  int tmp = a[col] + nowk - b[col] + 1;
  now = 1ll * now * inv[tmp] % P;
  b[col]--;
}
int main() {
  read(n), read(m), read(q);
  for (int i = 1; i <= n; i++)
      read(e[i]), a[e[i]]++;
  for (int i = 1; i <= q; i++)
      read(qry[i].l), read(qry[i].r), read(qry[i].k), qry[i].home = i;
  for (int i = 1; i < MAXN; i++)
      inv[i] = power(i, P - 2);
  sort(qry + 1, qry + q + 1, cmp);
  for (int i = 1, nxt; i <= q; i = nxt) {
      nxt = i; while (nxt <= q && qry[nxt].k == qry[i].k) nxt++;
      int cntq = nxt - i, tmp = 1ll * qry[i].k * m % P;
      size = max(n / sqrt(cntq), 1.0), blocks = 0;
      prod[0] = 1;
      for (int j = 1; j <= n; j++) {
          prod[j] = 1ll * prod[j - 1] * (tmp + j) % P;
          if (j % size == 1 % size) blocks++;
          block[j] = blocks;
      }
      sort(qry + i, qry + nxt, cnp);
      int l = qry[i].l, r = qry[i].l - 1;
      now = 1, nowk = qry[i].k;
      memset(b, 0, sizeof(b));
      for (int j = i; j < nxt; j++) {
          while (r < qry[j].r) add(e[++r]);
          while (l > qry[j].l) add(e[--l]);
          while (r > qry[j].r) del(e[r--]);
          while (l < qry[j].l) del(e[l++]);
          ans[qry[j].home] = 1ll * now * prod[n - (qry[j].r - qry[j].l + 1)] % P;
      }
  }
  for (int i = 1; i <= q; i++)
      writeln(ans[i]);
  return 0;
}

猜你喜欢

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