Atcoder Grand Contest 029 简要题解

Irreversible operation

求逆序对。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  string s;
  cin >> s;
  long long answer = 0;
  int current = 0;
  for (auto c : s) {
    if (c == 'B') {
      ++current;
    } else {
      answer += current;
    }
  }
  cout << answer << endl;
  return 0;
}

Powers of two

从大到小贪心即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  sort(a.begin(), a.end());
  vector<int> b(n);
  for (int i = n - 1; ~i; --i) {
    b[i] = 1;
    while (b[i] <= a[i]) {
      b[i] <<= 1;
    }
    b[i] -= a[i];
  }
  int answer = 0;
  map<int, int> c;
  for (int i = 0; i < n; ++i) {
    ++c[a[i]];
  }
  for (int i = n - 1; ~i; --i) {
    if (c[a[i]]) {
      --c[a[i]];
      if (c[b[i]]) {
        --c[b[i]];
        ++answer;
      }
    }
  }
  printf("%d\n", answer);
  return 0;
}

Lexicographic constraints

二分答案,如果 a i < a i + 1 a_i<a_{i+1} ai<ai+1 s i + 1 s_{i+1} si+1 s i s_i si 后面添若干个 0 0 0 ,否则去掉 a i + 1 a_{i+1} ai+1 之后的位然后加 1 1 1 ,拿个map模拟进位即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<int> a(n);
  for (int i = 0; i < n; ++i) {
    scanf("%d", &a[i]);
  }
  auto check = [&](int b) {
    map<int, int> s;
    for (int i = 0; i < n - 1; ++i) {
      if (a[i] >= a[i + 1]) {
        while (!s.empty() && s.rbegin()->first >= a[i + 1]) {
          s.erase(--s.end());
        }
        for (int j = a[i + 1] - 1; ~j; --j) {
          ++s[j];
          if (s[j] == b) {
            s.erase(j);
          } else {
            break;
          }
          if (!j) {
            return false;
          }
        }
      }
    }
    return true;
  };
  bool flag = true;
  for (int i = 0; i < n - 1; ++i) {
    if (a[i] >= a[i + 1]) {
      flag = false;
      break;
    }
  }
  if (flag) {
    puts("1");
    return 0;
  }
  int l = 2, r = n + 1;
  while (l < r) {
    int mid = l + r >> 1;
    if (check(mid)) {
      r = mid;
    } else {
      l = mid + 1;
    }
  }
  printf("%d\n", r);
  return 0;
}

Grid game

显然第一个人会一直向下走,假设当前第二个人能到 [ 1 , r ] [1,r] [1,r] ,如果下一行 [ 1 , r ] [1,r] [1,r] 有障碍,那么游戏就结束了;如果 r + 1 r+1 r+1 没有障碍,则可以往右走一步,变成 [ 1 , r + 1 ] [1,r+1] [1,r+1]

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n, m, q;
  scanf("%d %d %d", &n, &m, &q);
  vector<int> mins(n, m);
  for (int i = 0; i < q; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    mins[x] = min(mins[x], y);
  }
  int right = 0;
  for (int i = 1; i < n; ++i) {
    if (right >= mins[i]) {
      printf("%d\n", i);
      return 0;
    }
    if (right + 1 < mins[i]) {
      ++right;
    }
  }
  printf("%d\n", n);
  return 0;
}

Wandering TKHS

注意到 x x x 的答案大概只跟 1 1 1 x x x 的链前缀最大值的位置相关, 并且 x x x 能到的一定是 p a r e n t x parent_x parentx 能到的超集,对答案差分之后枚举每个前缀最大值算算贡献就行了。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
    int x, y;
    scanf("%d %d", &x, &y);
    --x;
    --y;
    adj[x].push_back(y);
    adj[y].push_back(x);
  }
  vector<int> up(n), parent(n, -1);
  function<void(int)> dfs = [&](int x) {
    if (x) {
      up[x] = max(up[parent[x]], parent[x]);
    }
    for (auto y : adj[x]) {
      if (y != parent[x]) {
        parent[y] = x;
        dfs(y);
      }
    }
  };
  dfs(0);
  vector<int> p(n), size(n);
  for (int i = 0; i < n; ++i) {
    p[i] = i;
    if (i) {
      size[i] = 1;
    }
  }
  auto find = [&](int x) {
    while (x != p[x]) {
      x = p[x] = p[p[x]];
    }
    return x;
  };
  vector<vector<int>> sub(n);
  for (int i = 1; i < n; ++i) {
    sub[up[i]].push_back(i);
  }
  vector<int> value(n), answer(n);
  for (int i = 0; i < n; ++i) {
    for (auto x : sub[i]) {
      if (x < i) {
        answer[x] = size[find(x)];
      } else {
        answer[x] = 1;
        for (auto y : adj[x]) {
          if (y < i) {
            if (y != parent[x]) {
              value[y] = size[find(y)];
            }
            answer[x] += size[find(y)];
          }
        }
      }
    }
    for (auto j : adj[i]) {
      if (j < i) {
        int x = find(i), y = find(j);
        size[x] += size[y];
        p[y] = x;
      }
    }
  }
  vector<int> pos(n);
  vector<int> st;
  function<void(int)> solve = [&](int x) {
    pos[x] = st.size();
    st.push_back(x);
    if (x) {
      answer[x] += answer[up[x]] - value[st[pos[up[x]] + 1]];
    }
    for (auto y : adj[x]) {
      if (y != parent[x]) {
        solve(y);
      }
    }
    st.pop_back();
  };
  solve(0);
  for (int i = 1; i < n; ++i) {
    printf("%d%c", answer[i], i == n - 1 ? '\n' : ' ');
  }
  return 0;
}

Construction of a tree

假设 1 1 1 是根,每个集合选出的是 ( u i , p a r e n t u i ) (u_i, parent_{u_i}) (ui,parentui) ,那么 u i u_i ui 一定是一个 [ 2 , n ] [2,n] [2,n] 的排列,用网络流求出这样的一组 u i u_i ui ,然后贪心构造 p a r e n t u i parent_{u_i} parentui :初始将 1 1 1 加入队列,然后如果某个集合有 1 1 1 ,那直接将 p a r e n t u i parent_{u_i} parentui 设为 1 1 1 并把 u i u_i ui 加入队列。考虑这时候如果无解,说明初始的图并不连通,所以任意求一组 u i u_i ui 都不会影响答案。

#include 

using namespace std;

const int inf = 0x3f3f3f3f;

namespace flow {
struct edge_t {
  int to, cap, rev;

  edge_t(int t, int c, int r) {
    to = t;
    cap = c;
    rev = r;
  }
};

int n, source, sink, answer;
vector<vector<edge_t>> adj;
vector<int> dist, current;

void init(int v, int s, int t) {
  n = v;
  source = s;
  sink = t;
  answer = 0;
  adj.clear();
  adj.resize(n);
  dist.resize(n);
  current.resize(n);
}

void add(int x, int y, int c) {
  adj[x].push_back(edge_t(y, c, adj[y].size()));
  adj[y].push_back(edge_t(x, 0, adj[x].size() - 1));
}

bool bfs() {
  queue<int> q;
  for (int i = 0; i < n; ++i) {
    dist[i] = -1;
  }
  dist[source] = 0;
  q.push(source);
  while (!q.empty()) {
    int x = q.front();
    q.pop();
    for (auto e : adj[x]) {
      if (e.cap && !~dist[e.to]) {
        dist[e.to] = dist[x] + 1;
        if (e.to == sink) {
          return true;
        }
        q.push(e.to);
      }
    }
  }
  return false;
}

int dfs(int x, int f) {
  if (x == sink) {
    return f;
  }
  for (int &i = current[x]; ~i; --i) {
    edge_t &e = adj[x][i];
    if (e.cap && dist[e.to] == dist[x] + 1) {
      int w = dfs(e.to, min(e.cap, f));
      if (w) {
        e.cap -= w;
        adj[e.to][e.rev].cap += w;
        return w;
      }
    }
  }
  return 0;
}

int max_flow() {
  while (bfs()) {
    for (int i = 0; i < n; ++i) {
      current[i] = adj[i].size() - 1;
    }
    while (true) {
      int flow = dfs(source, inf);
      if (!flow) {
        break;
      }
      answer += flow;
    }
  }
  return answer;
}
}

using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::adj;
using flow::max_flow;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  int n;
  scanf("%d", &n);
  vector<vector<int>> a(n - 1);
  vector<vector<int>> v(n);
  init(n * 2, n * 2 - 2, n * 2 - 1);
  for (int i = 0; i < n - 1; ++i) {
    int s;
    scanf("%d", &s);
    a[i].resize(s);
    add(source, i, 1);
    for (int j = 0; j < s; ++j) {
      scanf("%d", &a[i][j]);
      --a[i][j];
      v[a[i][j]].push_back(i);
      if (a[i][j]) {
        add(i, a[i][j] - 1 + n - 1, 1);
      }
    }
  }
  for (int i = 0; i < n - 1; ++i) {
    add(i + n - 1, sink, 1);
  }
  if (max_flow() != n - 1) {
    puts("-1");
    return 0;
  }
  vector<int> match(n - 1);
  for (int i = 0; i < n - 1; ++i) {
    for (auto e : adj[i]) {
      if (e.to < n * 2 - 2 && !e.cap) {
        match[i] = e.to - (n - 1) + 1;
      }
    }
  }
  vector<bool> visit(n);
  vector<int> parent(n - 1);
  int cc = 0;
  function<void(int)> insert = [&](int x) {
    ++cc;
    visit[x] = true;
    for (auto i : v[x]) {
      if (!visit[match[i]]) {
        parent[i] = x;
        insert(match[i]);
      }
    }
  };
  insert(0);
  if (cc != n) {
    puts("-1");
    return 0;
  }
  for (int i = 0; i < n - 1; ++i) {
    printf("%d %d\n", match[i] + 1, parent[i] + 1);
  }
  return 0;
}

你可能感兴趣的:(Atcoder Grand Contest 029 简要题解)