Codeforces Round #423 简要题解

版权声明:本文为博主原创文章,在标注作者的情况下可自由转载。 https://blog.csdn.net/EI_Captain/article/details/88208333

比赛链接

如果没有悲伤 世界是否还会美丽?
你的路往南走 我的心往北去
——幼稚园杀手《悲情城市 2017》

A String Reconstruction

开一个队列,在扫动的时候惰性检查对首是否能管住我再扔掉就行了。复杂度线性。

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

#include <algorithm>
#include <iostream>
#include <numeric>
#include <limits>
#include <functional>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <queue>

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

using namespace std;

const int L = 2000010, N = 100010;

int n, l;
vector<int> tag[L];
int ll[N];
char* pos[N];
char s[L], sv[L];

int main() {
  scanf("%d", &n);
  char* p = sv;
  for (int i = 1; i <= n; ++i) {
    scanf(" %s", p);
    pos[i] = p;
    int len = strlen(p);
    ll[i] = len;
    p += len + 1;
    int c;
    scanf("%d", &c);
    while (c--) {
      int t;
      scanf("%d", &t);
      tag[t].push_back(i);
      l = max(l, t + len - 1);
    }
  }
  queue<pair<int, int>> q;
  for (int i = 1; i <= l; ++i) {
    for (const auto& v : tag[i])
      q.push(make_pair(i + ll[v] - 1, v));
    while (!q.empty() && q.front().first < i)
      q.pop();
    if (q.empty())
      s[i] = 'a';
    else {
      int ed = q.front().first, v = q.front().second;
      s[i] = pos[v][ll[v] - (ed - i) - 1];
    }
  }
  puts(s + 1);
}

B Highroads

建成各深度尽量相同的菊花图,讨论一下就行。复杂度线性。

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

#include <algorithm>
#include <iostream>
#include <numeric>
#include <limits>
#include <functional>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <queue>

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

using namespace std;

typedef long long ll;

int main() {
  int n, k;
  scanf("%d%d", &n, &k);
  int v = n - 1;
  int l = v / k, b = v % k, a = k - b;
  if (b == 0) {
    printf("%d\n", l * 2);
  } else if (b == 1)
    printf("%d\n", l * 2 + 1);
  else
    printf("%d\n", l * 2 + 2);
  int cnt = 1;
  while (a--) {
    printf("%d %d\n", 1, ++cnt);
    for (int rep = 1; rep < l; ++rep) {
      int cur = cnt;
      printf("%d %d\n", cur, ++cnt);
    }
  }
  while (b--) {
    printf("%d %d\n", 1, ++cnt);
    for (int rep = 1; rep <= l; ++rep) {
      int cur = cnt;
      printf("%d %d\n", cur, ++cnt);
    }
  }

  return 0;
}

C DNA Evolution

按同余字符串长度分类,树状数组即可维护。时间 Θ ( ( n + m ) max e log n ) \Theta((n+m)\max |e|\log n) ,空间 Θ ( n max e ) \Theta(n\max |e|)

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

#include <algorithm>
#include <iostream>
#include <numeric>
#include <limits>
#include <functional>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <queue>

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

using namespace std;

typedef long long ll;

const int N = 101000, L = 11;

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

void ch(int* fw, int n, 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;
}

char* key = "ATGC";

int n;
char s[N], tmp[L];
int mp[256];
int fw[L][4][N];

void apply(int k, int v, int x) {
  for (int mod = 1; mod < L; ++mod) {
    ch(fw[mod][v] + (k % mod) * (n / mod + 3), n / mod + 1, (k - 1) / mod + 1, x);
  }
}

int qry(int mod, int v, int res, int r) {
  if (res == 0)
    res = mod;
  int p = (r - res + mod) / mod;
  res %= mod;
  return qry(fw[mod][v] + res * (n / mod + 3), p);
}

int main() {
  for (int i = 0; i < 4; ++i)
    mp[key[i]] = i;
  scanf("%s", s + 1);
  n = strlen(s + 1);
  for (int i = 1; i <= n; ++i)
    apply(i, mp[s[i]], 1);
  int q;
  scanf("%d", &q);
  while (q--) {
    int opt, x, y;
    scanf("%d%d", &opt, &x);
    if (opt == 1) {
      char c;
      scanf(" %c", &c);
      apply(x, mp[s[x]], -1);
      apply(x, mp[s[x] = c], 1);
    } else {
      scanf("%d%s", &y, tmp);
      int m = strlen(tmp);
      int ans = 0;
      for (int i = 0; i < m; ++i) {
        int r = (x + i) % m;
        ans += qry(m, mp[tmp[i]], r, y) - qry(m, mp[tmp[i]], r, x - 1);
      }
      printf("%d\n", ans);
    }
  }
}

D Best Edge Weight

随便找一颗 MST,在树外的边答案就是在 MST 上两点经过 max w 1 \max w -1 ,树边的答案由树上路径经过它的树外边的最小值给出。前者倍增直接找,后者可以把边按 w w 排序后用并查集来快速修改。时间复杂度 Θ ( m log m ) \Theta(m\log m)

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

#include <algorithm>
#include <iostream>
#include <numeric>
#include <limits>
#include <functional>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <queue>

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

using namespace std;

typedef long long ll;

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

struct Nde {
  int u, v, w, id;
  
  bool operator<(const Nde& rhs) const {
    return w < rhs.w;
  }
};

const int N = 200010, L = 18;

int n, m;
int prt[N][L], mx[N][L];
int val[N];
int ans[N], f[N], dep[N];
bool vis[N];
Nde ed[N];
E* g[N];

int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }

void adde(int u, int v, int w);
void dfs(int u);
pair<int, int> qry(int u, int v);
void flush(int u, int d, int w);

int main() {
  scanf("%d%d", &n, &m);
  iota(f + 1, f + n + 1, 1);
  for (int i = 1; i <= m; ++i) {
    scanf("%d%d%d", &ed[i].u, &ed[i].v, &ed[i].w);
    ed[i].id = i;
  }
  sort(ed + 1, ed + m + 1);
  for (int i = 1; i <= m; ++i) {
    int x = find(ed[i].u), y = find(ed[i].v);
    if (x != y) {
      f[x] = y;
      adde(ed[i].u, ed[i].v, ed[i].w);
      adde(ed[i].v, ed[i].u, ed[i].w);
    }
  }
  dfs(1);
  iota(f + 1, f + n + 1, 1);
  for (int i = 1; i <= m; ++i) {
    if (prt[ed[i].u][0] == ed[i].v || prt[ed[i].v][0] == ed[i].u)
      continue;
    int lca, cans;
    tie(cans, lca) = qry(ed[i].u, ed[i].v);
    ans[ed[i].id] = cans - 1;
    flush(ed[i].u, dep[lca], ed[i].w);
    flush(ed[i].v, dep[lca], ed[i].w);
  }
  for (int i = 1; i <= m; ++i)
    if (prt[ed[i].u][0] == ed[i].v || prt[ed[i].v][0] == ed[i].u) {
      int u = ed[i].u, v = ed[i].v;
      if (prt[u][0] == v)
        swap(u, v);
      ans[ed[i].id] = val[v] - 1;
    }
  for (int i = 1; i <= m; ++i)
    printf("%d ", ans[i]);
  
  return 0;
}

void flush(int u, int d, int w) {
  u = find(u);
  while (dep[u] > d) {
    val[u] = w;
    f[u] = prt[u][0];
    u = find(u);
  }
}

pair<int, int> qry(int u, int v) {
  int ret = 0;
  if (dep[u] < dep[v])
    swap(u, v);
  for (int k = L - 1; k >= 0; --k)
    if ((dep[u] - dep[v]) >> k & 1) {
      ret = max(ret, mx[u][k]);
      u = prt[u][k];
    }
  if (u == v)
    return make_pair(ret, u);
  for (int k = L - 1; k >= 0; --k)
    if (prt[u][k] != prt[v][k]) {
      ret = max({ret, mx[u][k], mx[v][k]});
      u = prt[u][k];
      v = prt[v][k];
    }
  return make_pair(max({ret, mx[u][0], mx[v][0]}), prt[u][0]);
}

void dfs(int u) {
  vis[u] = true;
  for (int i = 1; i < L; ++i) {
    prt[u][i] = prt[prt[u][i - 1]][i - 1];
    mx[u][i] = max(mx[u][i - 1], mx[prt[u][i - 1]][i - 1]);
  }
  for (E* p = g[u]; p; p = p->next)
    if (!vis[p->v]) {
      dep[p->v] = dep[u] + 1;
      prt[p->v][0] = u;
      mx[p->v][0] = p->w;
      dfs(p->v);
    }
}

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

E Rusty String

对答案造成阻碍的当一对 VK 的坐标为 i , j i,j 时,显然它周期不可能是 i j |i-j| 的因子。于是就是一个翻转后的卷积。最后枚举倍数检查是否合法,总时间 Θ ( n log n ) \Theta(n\log n)

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

#include <algorithm>
#include <iostream>
#include <numeric>
#include <limits>
#include <functional>
#include <stack>
#include <vector>
#include <complex>
#include <set>
#include <map>
#include <queue>

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

using namespace std;

typedef long long ll;

const int L = 21;

const int P = 998244353, R = 3;

int a[1 << L], b[1 << L];
bool res[1 << L], ans[1 << L];


void ntt(int* arr, int lgn, bool inv);
int mpow(int a, int k);
void exGcd(int a, int b, int& x, int& y);
int rev(int a, int p = P);
char s[1 << L];

int main() {
  int t;
  scanf("%d", &t);
  while (t--) {
    int n;
    scanf("%d%s", &n, s + 1);
    int l = 1;
    while ((1 << l) <= n)
      ++l;
    ++l;
    memset(a, 0, sizeof(int) << l);
    memset(b, 0, sizeof(int) << l);
    memset(res, 0, sizeof(bool) << l);
    for (int i = 1; i <= n; ++i) {
      if (s[i] == 'V')
        a[i - 1] = 1;
      else if (s[i] == 'K')
        b[n - i] = 1;
    }
    ntt(a, l, false);
    ntt(b, l, false);
    for (int i = 0; i < 1 << l; ++i)
      a[i] = a[i] * (ll)b[i] % P;
    ntt(a, l, true);
    for (int i = 0; i < 1 << l; ++i)
      if (a[i]) {
        int delt = i - n + 1;
        res[abs(delt)] = true;
      }
    for (int i = 1; i <= n; ++i) {
      ans[i] = true;
      for (int j = i; j < n; j += i)
        if (res[j]) {
          ans[i] = false;
          break;
        }
    }
    printf("%lu\n", count(ans + 1, ans + n + 1, true));
    for (int i = 1; i <= n; ++i)
      if (ans[i])
        printf("%d ", i);
    putchar('\n');
  }
}

int mpow(int a, int k) {
  int ret = 1;
  while (k) {
    if (k & 1)
      ret = (ll)ret * a % P;
    a = a * (ll)a % P;
    k >>= 1;
  }
  return ret;
}

void exGcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1;
    y = 0;
    return;
  }
  exGcd(b, a % b, y, x);
  y -= a / b * x;
}

int rev(int a, int p) {
  int x, y;
  exGcd(a, p, x, y);
  if (x < 0)
    x += p;
  return x;
}

void ntt(int* arr, int lgn, bool inv) {
  static int rev[1 << L];
  int n = 1 << lgn;
  for (int i = 1; i < n; ++i)
    rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lgn - 1));
  for (int i = 0; i < n; ++i)
    if (i < rev[i])
      swap(arr[i], arr[rev[i]]);
  int rt = inv ? ::rev(R) : R;
  for (int i = 0; i < lgn; ++i) {
    int t = 1 << i, g = mpow(rt, (P - 1) >> (i + 1));
    for (int j = 0; j < n; j += t << 1) {
      int w = 1;
      for (int k = j; k < j + t; ++k) {
        int a0 = arr[k], a1 = arr[k + t];
        arr[k] = (a0 + a1 * (ll)w) % P;
        arr[k + t] = (a0 + a1 * (ll)(P - w)) % P;
        w = w * (ll)g % P;
      }
    }
  }
  if (inv) {
    int rm = ::rev(n);
    for (int i = 0; i < n; ++i)
      arr[i] = arr[i] * (ll)rm % P;
  }
}

F Dirty Arkady’s Kitchen

注意到图是无向图,这意味着可以在一条边上来回走。特判一下 n = 1 n = 1 的情况,其余情况不关注某个点,而是关注某个点上可以来回走的连续时间段,这些段是由它的连边贡献的,因此总量是 Θ ( m ) \Theta(m) ,对于距离的奇偶性拆点跑最短路。实现地精细一点保证每个时间段只被松弛一次,从而每条边只用于松弛 Θ ( 1 ) \Theta(1) 次。复杂度 Θ ( m log m ) \Theta(m\log m)

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

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

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

struct Node {
  int u, step;

  Node(int u, int step) : u(u), step(step) {}

  bool operator>(const Node& rhs) const { return step > rhs.step; }
};

const int N = 500010;

int n;
vector<tuple<int, int, int>> rg[N];
vector<int> hl[N][2], hr[N][2], dis[N][2];
vector<vector<tuple<int, int, int>>> adj[N][2];

void adde(int u, int v, int l, int r);
bool cmp(const tuple<int, int, int>& lhs, const tuple<int, int, int>& rhs) {
  if (get<1>(lhs) != get<1>(rhs))
    return get<1>(lhs) < get<1>(rhs);
  return get<2>(lhs) < get<2>(rhs);
}

int main() {

  int m;
  scanf("%d%d", &n, &m);
  while (m--) {
    int u, v, l, r;
    scanf("%d%d%d%d", &u, &v, &l, &r);
    adde(u, v, l, r);
    adde(v, u, l, r);
  }
  if (rg[1].empty()) {
    puts(n == 1 ? "0" : "-1");
  } else {
    for (int i = 1; i <= n; ++i) {
      sort(rg[i].begin(), rg[i].end(), cmp);
      for (int j = 0; j < 2; ++j) {
        for (const auto& tup : rg[i]) {
          int l, r;
          tie(ignore, l, r) = tup;
          if ((l & 1) != j)
            ++l;
          if ((r & 1) != j)
            --r;
          if (l > r)
            continue;
          if (hl[i][j].empty() || hr[i][j].back() < l) {
            hl[i][j].push_back(l);
            hr[i][j].push_back(r);
            adj[i][j].push_back(vector<tuple<int, int, int>>(1, tup));
          } else {
            hr[i][j].back() = max(hr[i][j].back(), r);
            adj[i][j].back().push_back(tup);
          }
        }
        dis[i][j] = vector<int>(hl[i][j].size(), -1);
      }
    }
    priority_queue<Node, vector<Node>, greater<Node>> q;
    int ans = -1;
    q.emplace(1, 0);
    while (!q.empty()) {
      Node tmp = q.top();
      q.pop();
      if (tmp.u == n) {
        ans = tmp.step;
        break;
      }
      int u = tmp.u, j = tmp.step & 1;
      int i = int(upper_bound(hl[u][j].begin(), hl[u][j].end(), tmp.step) - hl[u][j].begin()) - 1;
      if (i < 0 || hl[u][j][i] > tmp.step || dis[u][j][i] != -1 && (dis[u][j][i] <= tmp.step))
        continue;
      dis[u][j][i] = tmp.step;
      for (const auto& tup : adj[u][j][i]) {
        int v, l, r;
        tie(v, l, r) = tup;
        if (r <= tmp.step)
          continue;
        int dist;
        if (l <= tmp.step)
          dist = tmp.step + 1;
        else {
          dist = (l - tmp.step + 1) / 2 * 2 + tmp.step;
          if (dist >= r)
            continue;
          ++dist;
        }
        q.emplace(v, dist);
      }
    }
    printf("%d\n", ans);
  }

  return 0;
}

void adde(int u, int v, int l, int r) {
  rg[u].emplace_back(v, l, r);
}

猜你喜欢

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