Codeforces Round #545 (Div. 1) 简要题解

比赛链接
卡系统栈的出题人应该拉出去祭天

A Skyscrapers

找出该行不同数的数量和数在该行的排名,那么这个数应该被赋值成行列排名的较大值,剩下的数往上延伸。主要排个序就行了。 Θ ( n m log ( n m ) ) \Theta(nm\log (nm))

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>

#ifdef LBT

#define LOG(FMT...) fprintf(stderr, FMT)

#else

#define LOG(FMT...)

#endif

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 1010;

int n, m;
int a[N][N], x[N][N], y[N][N], r[N], c[N];
int tmp[N], res[N];

void grank(int n) {
  static int st[N];
  memcpy(st, tmp, sizeof(st));
  sort(st + 1, st + n + 1);
  int cnt = unique(st + 1, st + n + 1) - st - 1;
  for (int i = 1; i <= n; ++i)
    res[i] = lower_bound(st + 1, st + cnt + 1, tmp[i]) - st;
}

int main() {
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      scanf("%d", &a[i][j]);
  for (int i = 1; i <= n; ++i) {
    memcpy(tmp, a[i], sizeof(tmp));
    grank(m);
    memcpy(x[i], res, sizeof(res));
    r[i] = *max_element(res + 1, res + m + 1);
  }
  for (int j = 1; j <= m; ++j) {
    for (int i = 1; i <= n; ++i)
      tmp[i] = a[i][j];
    grank(n);
    for (int i = 1; i <= n; ++i)
      y[i][j] = res[i];
    c[j] = *max_element(res + 1, res + n + 1);
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= m; ++j) {
      int mx = max(x[i][j], y[i][j]);
      mx += max(r[i] - x[i][j], c[j] - y[i][j]);
      printf("%d ", mx);
    }
    putchar('\n');
  }
  return 0;
}

B Camp Schedule

应该尽量先填进去一串,然后是每次补尽量少的子使得串数 +1,那就应该补 KMP 跑出来的 T n e x t ( T ) |T| - next(|T|) 的那段。算一下剩下的 1 够不够用。 Θ ( S + T ) \Theta(|S| + |T|)

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>

#ifdef LBT

#define LOG(FMT...) fprintf(stderr, FMT)

#else

#define LOG(FMT...)

#endif

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 500010;

int n, m;
int nxt[N];
char s[N], t[N];

int main() {
  scanf("%s%s", s + 1, t + 1);
  n = strlen(s + 1);
  m = strlen(t + 1);
  for (int i = 2; i <= m; ++i) {
    int k = nxt[i - 1];
    while (k && t[k + 1] != t[i])
      k = nxt[k];
    if (t[k + 1] == t[i])
      ++k;
    nxt[i] = k;
  }
  int c1 = count(s + 1, s + n + 1, '1'), c2 = count(t + 1, t + m + 1, '1');
  int cyc = m - nxt[m], cc = count(t + nxt[m] + 1, t + m + 1, '1');
  int ans = 0;
  for (int i = 1; i <= n; ++i) {
    if (m + (i - 1) * cyc > n)
      break;
    ll res = n - (i - 1) * (ll)cyc - m, remain1 = c1 - cc * (ll)(i - 1) - c2;
    if (remain1 < 0 || remain1 > res)
      continue;
    ans = i;
  }
  if (ans == 0)
    puts(s + 1);
  else {
    printf("%s", t + 1);
    --ans;
    c1 -= c2;
    n -= m;
    while (ans--) {
      printf("%s", t + nxt[m] + 1);
      n -= cyc;
      c1 -= cc;
    }
    while (c1--) {
      putchar('1');
      n--;
    }
    while (n--)
      putchar('0');
  }
  return 0;
}

C Museums Tour

拆点成每个博物馆以及其在一个星期的第几天到达。只需注意到一个 SCC 如果能到达另一个 SCC,那么它们不可能包含同一个博物馆。因此答案不会重复。 Θ ( d ( n + m ) ) \Theta(d(n+m)) 跑 500w 的 tarjan 要手写栈
在原图上跑一个 SCC 之后在 SCC 内通过拆点 BFS 就只可能递归 10w 了

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <tuple>
#include <stack>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>

#ifdef LBT

#define LOG(FMT...) fprintf(stderr, FMT)

#else

#define LOG(FMT...)

#endif

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 100010, D = 50;

int n, d, sc;
int dfn[N * D], low[N * D], good[N * D], belongs[N * D], nxt[N * D];
int ev[N], enxt[N];
bool instk[N * D], use[N];
char s[N][D];
int ans[N * D], seq[N * D];
int g[N];

int norm(int x) { return x >= d ? (x - d) : x; }
int gu(int u, int k) { return (u - 1) * d + k + 1; }
void tarjan();
int dfs2(int u);
void adde(int u, int v);

int main() {
  int m;
  scanf("%d%d%d", &n, &m, &d);
  while (m--) {
    int u, v;
    scanf("%d%d", &u, &v);
    adde(u, v);
  }
  for (int i = 1; i <= n; ++i) {
    scanf("%s", s[i]);
  }
  tarjan();
  memset(ans, -1, sizeof(ans));
  for (int i = 1; i <= sc; ++i)
      dfs2(seq[i]);
  printf("%d\n", ans[1]);
  return 0;
}

int dfs2(int u) {
  ans[u] = 0;
  for (int v = u; v; v = nxt[v]) {
    for (int i = g[(v - 1) / d + 1]; i; i = enxt[i]) {
      if (belongs[gu(ev[i], norm((v - 1) % d + 1))] != u)
        ans[u] = max(ans[u], ans[belongs[gu(ev[i], norm((v - 1) % d + 1))]]);
    }
  }
  return ans[u] += good[u];
}

void tarjan() {
  static int t, top, fc;
  static int stk[N * D];
  static int fi[N * D], fuu[N * D];
  fuu[++fc] = 1;
  fi[fc] = -1;
  while (fc) {
    int uu = fuu[fc], u = (uu - 1) / d + 1, k = (uu - 1) % d;
    int i = fi[fc];
    if (i == -1) {
      dfn[uu] = low[uu] = ++t;
      stk[++top] = uu;
      instk[uu] = true;
      i = g[u];
    } else {
      low[uu] = min(low[uu], low[gu(ev[i], norm(k + 1))]);
      i = enxt[i];
    }
    for (; i; i = enxt[i]) {
      int vv = gu(ev[i], norm(k + 1));
      if (!dfn[vv]) {
        ++fc;
        fuu[fc] = vv;
        fi[fc] = -1;
        break;
      } else if (instk[vv])
        low[uu] = min(low[vv], low[uu]);
    }
    if (i) {
      fi[fc - 1] = i;
      continue;
    }
    if (dfn[uu] == low[uu]) {
      int v;
      int tmp = top;
      do {
        v = stk[top--];
        instk[v] = false;
        low[v] = dfn[uu];
        belongs[v] = uu;
      } while (uu != v);
      int cost = 0;
      for (int i = top + 1; i <= tmp; ++i) {
        int v = stk[i];
        int u = (v - 1) / d + 1, k = (v - 1) % d;
        if (s[u][k] == '1') {
          if (!use[u]) {
            ++cost;
            use[u] = true;
          }
        }
        if (i < tmp)
          nxt[v] = stk[i + 1];
      }
      for (int i = top + 1; i <= tmp; ++i) {
        int v = stk[i];
        use[(v - 1) / d + 1] = false;
      }
      good[uu] = cost;
      seq[++sc] = uu;
    }
    --fc;
  }
}

void adde(int u, int v) {
  static int t;
  ++t;
  ev[t] = v;
  enxt[t] = g[u];
  g[u] = t;
}

D Cooperative Game

经典的 Floyd 判环法,先让一个人每次走 1 步,另一个人每次走 2 步,它们下次相遇在前者走 t + ( t &VeryThinSpace; m o d &VeryThinSpace; c ) t + (-t \bmod c) 步后,则已经经过了 &lt; 2 ( t + c ) &lt; 2(t + c) 次操作。接下来所有人一起走,再经过 t t 步后汇合与交点。总共不超过 3 t + 2 c 3t + 2c 次操作。
CF:只要我出的题够经典,以至于所有人都见过,那就不算原题

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>

#ifdef LBT

#define LOG(FMT...) fprintf(stderr, FMT)

#else

#define LOG(FMT...)

#endif

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int belongs[10];

void qry(const vector<int>& s) {
  printf("next");
  for (int v : s)
    printf(" %d", v);
  putchar('\n');
  fflush(stdout);
  int k;
  scanf("%d", &k);
  static char tmp[11];
  while (k--) {
    scanf("%s", tmp);
    for (char* p = tmp; *p; ++p)
      belongs[*p - '0'] = k;
  }
}

int main() {
  while (true) {
    qry({0, 1});
    qry({1});
    if (belongs[0] == belongs[1])
      break;
  }
  while (count(belongs, belongs + 10, 0) != 10)
    qry({0, 1, 2, 3, 4, 5, 6, 7 ,8 ,9});
  puts("done");
  return 0;
}

E Train Car Selection

加减一次函数可以斜率优化,维护一个两边可以插入节点的凸包。 Θ ( m log m ) \Theta(m\log m)

#include <cassert>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>

#include <algorithm>
#include <tuple>
#include <random>
#include <bitset>
#include <chrono>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <iostream>
#include <limits>
#include <numeric>

#ifdef LBT

#define LOG(FMT...) fprintf(stderr, FMT)

#else

#define LOG(FMT...)

#endif

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

typedef ll Num;
typedef long double BNum;

struct Vec {
	Num x, y;

	Vec() : x(), y() {}

	Vec(Num x, Num y) : x(x), y(y) {}

	bool operator<(const Vec &rhs) const {
		return x < rhs.x;
	}

	Vec operator+(const Vec& rhs) const { return Vec(x + rhs.x, y + rhs.y); }
	Vec operator-(const Vec& rhs) const { return Vec(x - rhs.x, y - rhs.y); }

	BNum operator*(const Vec& rhs) const { return x * (BNum) rhs.y - y * (BNum) rhs.x; }
	BNum operator^(const Vec& rhs) const { return x * (BNum) rhs.x + y * (BNum) rhs.y; }
};


const int N = 600010;

int l, r;
ll pos;
ll pl, pr;
ll lk, lb;
Vec hull[N];

ll gv(ll x, ll y) {
  return x * lk + lb + y;
}

ll gv(const Vec& v) {
  return gv(v.x, v.y);
}

int main() {
  int m;
  scanf("%lld%d", &pr, &m);
  l = m;
  r = m;
  hull[l] = Vec(0, 0);
  while (m--) {
    int x, y;
    scanf("%d%d", &x, &y);
    if (x == 1) {
      pl -= y;
      Vec ins(pl, -lk * pl - lb);
      while (r > l && (ins - hull[l + 1]) * (ins - hull[l]) >= 0)
        ++l;
      hull[--l] = ins;
    } else if (x == 2) {
      Vec ins(pr, -lk * pr - lb);
      pr += y;
      while (r > l && (ins - hull[r - 1]) * (hull[r] - hull[r - 1]) >= 0)
        --r;
      hull[++r] = ins;
    } else {
      int z;
      scanf("%d", &z);
      lb += y - z * pl;
      lk += z;
    }
    int ml = l, mr = r;
    while (ml < mr) {
      int mid = (ml + mr) >> 1;
      if (gv(hull[mid]) > gv(hull[mid + 1]))
        ml = mid + 1;
      else
        mr = mid;
    }
    printf("%lld %lld\n", hull[ml].x - pl + 1, gv(hull[ml]));
  }
  return 0;
}

F Matches Are Not a Child’s Play

注意到每次 up 操作时假设之前权值最大的节点是 u u ,然后 up v v ,那么顺序上就是其他的相对顺序不变,把 ( u , v ) (u,v) 路径上的点都变成最后弹出了。可以考虑每次修改就是将每个点维护其最后一次被哪个操作影响,用树状数组维护第 k k 次操作还剩多少点以其为最后一次,以及最初的顺序中有那些点还被保留。用树链剖分维护连续段的话容易做到 Θ ( n log n + q log 2 n ) \Theta(n\log n + q\log^2 n)

#include <cstdio>
#include <cstdlib>

#include <algorithm>
#include <functional>
#include <queue>
#include <map>

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

const int N = 200010;

struct E { int v; E* next; };

int n, cur = 1;
int ord[N], col[N], deg[N], fw1[N], fw2[N], beg[N];
int sub[N], pref[N], prt[N], dep[N], dfn[N], top[N], rev[N];
map<int, int> stk[N];
char opt[10];
E* g[N];

void adde(int u, int v) { static E pool[N * 2], *p = pool; p->v = v; p->next = g[u]; g[u] = p++; ++deg[u]; }

void dfs(int u) {
  sub[u] = 1;
  for (E* p = g[u]; p; p = p->next)
    if (!sub[p->v]) {
      prt[p->v] = u; dep[p->v] = dep[u] + 1; dfs(p->v);
      sub[u] += sub[p->v];
      pref[u] = sub[pref[u]] > sub[p->v] ? pref[u] : p->v;
    }
}

void hld(int u) {
  static int t;
  if (!top[u]) top[u] = u;
  dfn[u] = ++t; rev[t] = u;
  if (pref[u]) {
    top[pref[u]] = top[u]; hld(pref[u]);
    for (E* p = g[u]; p; p = p->next)
      if (!dfn[p->v]) hld(p->v);
  } else {
    stk[top[u]].insert(make_pair(dep[u], 1));
    stk[top[u]].insert(make_pair(dep[top[u]] - 1, 0));
  }
}

int lowBit(int k) { return k & -k; }

void ch(int* fw, int k, int x) { for (; k < N; k += lowBit(k)) fw[k] += x; }

int qry(int* fw, int k) { int ret = 0; for (; k; k -= lowBit(k)) ret += fw[k]; return ret; }

int dist(int u, int v) {
  int ret = 0, x = top[u], y = top[v];
  while (x != y) {
    if (dep[x] > dep[y]) swap(u, v), swap(x, y);
    ret += dep[v] - dep[y] + 1;
    v = prt[y]; y = top[v];
  }
  return ret + abs(dep[u] - dep[v]) + 1;
}

int when(int u) {
  int c = stk[top[u]].lower_bound(dep[u])->second;
  if (c == 1) return qry(fw1, ord[u]);
  return qry(fw2, c - 1) + dist(u, beg[c]);
}

void clr(int u, int d) {
  while (dep[u] >= d) {
    ch(fw1, ord[u], -1);
    u = prt[u];
  }
}

void replace(int t, int l, int r, int c) {
  map<int, int>& seg = stk[t];
  map<int, int>::iterator it = seg.lower_bound(r);
  bool f = false;
  while (true) {
    int cc = it->second;
    ch(fw2, cc, prev(it)->first - r);
    if (cc == 1)
      clr(rev[dfn[t] + r - dep[t]], max(prev(it)->first + 1, l));
    if (it->first == r) it->second = c;
    else it = seg.insert(make_pair(r, c)).first;
    if (!f)
      r = (--it)->first;
    else {
      seg.erase(it--);
      r = it->first;
    }
    if (r < l) {
      if (r < l - 1) {
        seg.insert(make_pair(l - 1, cc));
        ch(fw2, cc, l - r - 1);
      }
      return;
    }
    f = true;
  }
}

void replace(int u, int v, int c) {
  int x = top[u], y = top[v];
  while (x != y) {
    if (dep[x] > dep[y]) swap(u, v), swap(x, y);
    replace(y, dep[y], dep[v], c);
    v = prt[y]; y = top[v];
  }
  if (dep[u] > dep[v]) swap(u, v);
  replace(x, dep[u], dep[v], c);
}

int main() {
  int q; scanf("%d%d", &n, &q);
  for (int rep = 1; rep < n; ++rep) {
    int u, v; scanf("%d%d", &u, &v);
    adde(u, v); adde(v, u);
  }
  priority_queue<int, vector<int>, greater<int> > pq;
  for (int i = 1; i <= n; ++i) if (deg[i] == 1) pq.push(i);
  int t = 0;
  while (!pq.empty()) {
    int u = pq.top(); pq.pop();
    ord[u] = ++t;
    for (E* p = g[u]; p; p = p->next)
      if (!ord[p->v] && --deg[p->v] == 1)
        pq.push(p->v);
  }
  dfs(1); hld(1);
  for (int i = 1; i <= n; ++i) ch(fw1, i, 1);
  ch(fw2, 1, n);
  int bef = n;
  dep[0] = -1;
  while (q--) {
    int x, y; scanf("%s%d", opt, &x);
    if (opt[0] == 'u') {
      if (x == bef) continue;
      beg[++cur] = bef;
      ch(fw2, cur, dist(bef, x));
      replace(x, bef, cur);
      bef = x;
    } else if (opt[0] == 'w') {
      printf("%d\n", when(x));
    } else {
      scanf("%d", &y);
      printf("%d\n", when(x) < when(y) ? x : y);
    }
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/EI_Captain/article/details/88372215