Atcoder Grand Contest 034 简要题解

Kenken Race

考虑两个人不互相影响的情况,显然只需要判断是否存在两个相邻的障碍即可;

当原来在前面的人要走到后面时,需要存在三个连续的空位来跳过去。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, a, b, c, d;
  string s;
  cin >> n >> a >> b >> c >> d >> s;
  --a;
  --b;
  --c;
  --d;
  auto check = [&](int a, int b) {
    for (int i = a; i < b; ++i) {
      if (s[i] == '#' && s[i + 1] == '#') {
        return false;
      }
    }
    return true;
  };
  if (!check(a, c) || !check(b, d)) {
    cout << "No" << "\n";
  } else if (c <= d) {
    cout << "Yes" << "\n";
  } else {
    for (int i = b; i <= d; ++i) {
      if (i - 1 >= 0 && i + 1 < n && s[i] == '.' && s[i - 1] == '.' && s[i + 1] == '.') {
        cout << "Yes" << "\n";
        return 0;
      }
    }
    cout << "No" << "\n";
  }
  return 0;
}

ABC

可以将一次操作看成将 A A A 往后移,不难发现能移就移是最优的,那么倒着扫一遍维护后面 B C BC BC 的个数即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  string s;
  cin >> s;
  reverse(s.begin(), s.end());
  long long ans = 0;
  int stage = 0;
  int back = 0;
  for (auto c : s) {
    if (c == 'A') {
      if (stage) {
        stage = back = 0;
      } else {
        ans += back;
      }
    } else if (c == 'B') {
      if (stage) {
        stage = 0;
        back++;
      } else {
        stage = back = 0;
      }
    } else {
      if (stage) {
        back = 0;
      } else {
        stage = 1;
      }
    }
  }
  cout << ans << "\n";
  return 0;
}

Tests

显然劣势的考试会选择 l l l ,优势的会选择 u u u 。由于贡献的导数是不降的,所以一定是选若干个喂满,至多有一个不喂;二分答案后判断即可。

#include 

using namespace std;

struct point {
  int b, l, u;
  long long s;
  
  bool operator < (const point &other) const {
    return s > other.s;
  }
};

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<point> a(n);
  long long need = 0;
  for (int i = 0; i < n; ++i) {
    cin >> a[i].b >> a[i].l >> a[i].u;
    need += (long long) a[i].b * a[i].l;
    a[i].s = (long long) a[i].l * a[i].b + (long long) a[i].u * (m - a[i].b);
  }
  sort(a.begin(), a.end());
  auto check = [&](long long t) {
    int full = t / m, small = t % m;
    long long sum = 0;
    for (int i = 0; i <= full; ++i) {
      sum += a[i].s;
    }
    long long ans = 0;
    for (int i = 0; i < n; ++i) {
      long long single = a[i].b <= small ? (long long) a[i].u * small - (long long) (a[i].u - a[i].l) * a[i].b : (long long) a[i].l * small;
      if (i <= full) {
        ans = max(ans, sum - a[i].s + single);
      } else {
        ans = max(ans, sum - a[full].s + single);
      }
    }
    return ans >= need;
  };
  long long l = 0, r = (long long) n * m;
  while (l < r) {
    long long mid = (l + r) >> 1;
    if (check(mid)) {
      r = mid;
    } else {
      l = mid + 1;
    }
  }
  cout << r << "\n";
  return 0;
}

Manhattan Max Matching

4 4 4 个点表示四种符号,优化费用流建图即可。

#include 

using namespace std;

using cap_t = int;
using cost_t = long long;

const cap_t cap_inf = 1 << 30;
const cost_t cost_inf = 1ll << 60;

namespace flow {
struct edge {
  int to, rev;
  cost_t cost;
  cap_t cap;

  edge(int t, cap_t c, cost_t w, int r) {
    to = t;
    rev = r;
    cap = c;
    cost = w;
  }
};

vector<vector<edge>> adj;
vector<cost_t> dist;
int n, source, sink;
vector<bool> visit;
vector<int> cur;
cost_t res;
cap_t ans;

void init(int v, int s, int t) {
  n = v;
  ans = res = 0;
  source = s;
  sink = t;
  adj.clear();
  adj.resize(n);
  cur.resize(n);
  dist.resize(n);
  visit.resize(n);
}

void add(int x, int y, cap_t c, cost_t w) {
  adj[x].emplace_back(y, c, w, adj[y].size());
  adj[y].emplace_back(x, 0, -w, adj[x].size() - 1);
}

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

cap_t dfs(int x, cap_t f) {
  if (x == sink) {
    return f;
  }
  visit[x] = true;
  cap_t res = 0;
  for (int &i = cur[x]; i < (int) adj[x].size(); ++i) {
    edge &e = adj[x][i];
    if (e.cap && dist[e.to] == dist[x] + e.cost && !visit[e.to]) {
      cap_t w = dfs(e.to, min(e.cap, f - res));
      res += w;
      e.cap -= w;
      adj[e.to][e.rev].cap += w;
      if (res == f) {
        visit[x] = false;
        return res;
      }
    }
  }
  dist[x] = cost_inf;
  return res;
}

pair<cap_t, cost_t> min_cost_max_flow() {
  while (spfa()) {
    cap_t flow = dfs(source, cap_inf);
    ans += flow;
    res += flow * dist[sink];
  }
  return make_pair(ans, res);
}
}

using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::min_cost_max_flow;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  init(n * 2 + 6, n * 2, n * 2 + 1);
  for (int i = 0; i < n; ++i) {
    int x, y, z;
    cin >> x >> y >> z;
    add(source, i, z, 0);
    add(i, n * 2 + 2, z, x + y);
    add(i, n * 2 + 3, z, x - y);
    add(i, n * 2 + 4, z, y - x);
    add(i, n * 2 + 5, z, -(x + y));
  }
  for (int i = 0; i < n; ++i) {
    int x, y, z;
    cin >> x >> y >> z;
    add(i + n, sink, z, 0);
    add(n * 2 + 2, i + n, z, -(x + y));
    add(n * 2 + 3, i + n, z, y - x);
    add(n * 2 + 4, i + n, z, x - y);
    add(n * 2 + 5, i + n, z, x + y);
  }
  cout << -min_cost_max_flow().second << "\n";
  return 0;
}

Complete Compress

枚举最后停留在哪个点,然后点只会往上移,用类似求重心的方法做个DP即可。

#include 

using namespace std;

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  string s;
  cin >> n >> s;
  vector<vector<int>> adj(n);
  for (int i = 0; i < n - 1; ++i) {
    int from, to;
    cin >> from >> to;
    --from;
    --to;
    adj[from].push_back(to);
    adj[to].push_back(from);
  }
  int ans = 1 << 30;
  for (int i = 0; i < n; ++i) {
    vector<int> size(n);
    vector<int> foo(n);
    vector<int> bar(n);
    function<void(int, int)> dfs = [&](int x, int p) {
      size[x] = s[x] == '1';
      vector<int> son;
      for (auto y : adj[x]) {
        if (y != p) {
          son.push_back(y);
          dfs(y, x);
          size[x] += size[y];
          foo[x] += foo[y];
        }
      }
      if (!son.empty()) {
        sort(son.begin(), son.end(), [&](int u, int v) {
          return foo[u] < foo[v];
        });
        int other = 0;
        for (int i = 0; i < (int) son.size() - 1; ++i) {
          other += foo[son[i]];
        }
        bar[x] = max(0, bar[son.back()] - other);
      }
      foo[x] += size[x];
      bar[x] += size[x];
    };
    dfs(i, -1);
    if (bar[i] == size[i] && (foo[i] - size[i]) % 2 == 0) {
      ans = min(ans, (foo[i] - size[i]) / 2);
    }
  }
  if (ans == 1 << 30) {
    cout << -1 << "\n";
  } else {
    cout << ans << "\n";
  }
  return 0;
}

RNG and XOR

首先将概率数组 p p p 进行 FWT。

考虑如何求 t t t 次操作后为 u u u 的概率,由 iFWT 的定义可知概率为 ∑ i ( − 1 ) p o p c o u n t ( i & u ) p i t \sum_i (-1)^{popcount(i\& u)} p_i^t i(1)popcount(i&u)pit ,写成母函数的形式,有 f [ u ] ( x ) = ∑ i ( − 1 ) p o p c o u n t ( i & u ) 1 1 − p i x f[u](x) = \sum_{i} (-1)^{popcount(i\&u)} \frac{1}{1-p_ix} f[u](x)=i(1)popcount(i&u)1pix1 ,而不难发现第一次到的概率就是 g [ u ] ( x ) = f [ u ] ( x ) / f [ 0 ] ( x ) g[u](x) =f[u](x) / f[0](x) g[u](x)=f[u](x)/f[0](x) ,我们要求的是 g [ u ] ′ ( 1 ) g[u]'(1) g[u](1) 的值,用 ( f / g ) ′ = ( f ′ g − g ′ f ) / g 2 (f/g)' = (f'g-g'f)/g^2 (f/g)=(fggf)/g2 计算即可。

注意这里会出现除 0 0 0 的情况,所以分子分母需要同时乘上 1 − x 1-x 1x 进行计算。

#include 

using namespace std;

const int md = 998244353;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline void sub(int &x, int y) {
  x -= y;
  if (x < 0) {
    x += md;
  }
}

inline int mul(int x, int y) {
  return (int) ((long long) x * y % md);
}

inline int inv(int a) {
  if (a < 0) {
    a += md;
  }
  int b = md, u = 0, v = 1;
  while (a) {
    int t = b / a;
    b -= t * a;
    swap(a, b);
    u -= t * v;
    swap(u, v);
  }
  if (u < 0) {
    u += md;
  }
  return u;
}

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n;
  cin >> n;
  vector<int> a(1 << n);
  int sum = 0;
  for (int i = 0; i < 1 << n; ++i) {
    cin >> a[i];
    sum += a[i];
  }
  sum = inv(sum);
  for (int i = 0; i < 1 << n; ++i) {
    a[i] = mul(a[i], sum);
  }
  for (int i = 1; i < 1 << n; i <<= 1) {
    for (int j = 0; j < 1 << n; j += i << 1) {
      for (int k = 0; k < i; ++k) {
        int u = a[j + k], v = a[j + k + i];
        a[j + k] = (u + v) % md;
        a[j + k + i] = (u - v + md) % md;
      }
    }
  }
  a[0] = 0;
  for (int i = 1; i < 1 << n; ++i) {
    a[i] = inv(a[i] - 1);
  }
  for (int i = 1; i < 1 << n; i <<= 1) {
    for (int j = 0; j < 1 << n; j += i << 1) {
      for (int k = 0; k < i; ++k) {
        int u = a[j + k], v = a[j + k + i];
        a[j + k] = (u + v) % md;
        a[j + k + i] = (u - v + md) % md;
      }
    }
  }
  cout << 0 << "\n";
  for (int i = 1; i < 1 << n; ++i) {
    sub(a[i], a[0]);
    cout << a[i] << "\n";
  }
  return 0;
}

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