JOI 2018 Final 简要题解

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

题面见 https://loj.ac/problems/tag/196,205

T1 寒冬暖炉

考虑每两个客人之间的时间间隔 Δ T i 1 \Delta T_i - 1 ,这 n 1 n-1 个关闭暖炉的机会中只能选择 n k n - k 个,故选择最大的 n k n-k 个。用 nth_element 即可 Θ ( n ) \Theta(n)

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

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

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 100010;

int n, k;
int t[N], a[N];

int main() {
#ifndef ONLINE_JUDGE
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d%d", &n, &k);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &t[i]);
  int ans = t[n] - t[1] + 1;
  for (int i = 1; i < n; ++i)
    a[i] = t[i + 1] - t[i] - 1;
  nth_element(a + 1, a + k, a + n, greater<int>());
  ans -= accumulate(a + 1, a + k, 0);
  printf("%d\n", ans);

#ifndef ONLINE_JUDGE
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

T2

A A 排序,这样答案必然是选择其中一个连续区间,答案拆成有前缀和的形式 ( S r + A r ) ( S l 1 + A l ) (S_r + A_r) - (S_{l-1} + A_l) ,扫动的时候维护前缀最值。 Θ ( n log n ) \Theta(n\log n)

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

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

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 500010;

int n;
pair<ll, int> art[N];

int main() {
#ifndef ONLINE_JUDGE
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d", &n);
  for (int i = 1; i <= n; ++i)
    scanf("%lld%d", &art[i].first, &art[i].second);
  sort(art + 1, art + n + 1);
  ll ans = 0, pre = -1LL << 60, s = 0;
  for (int i = 1; i <= n; ++i) {
    pre = max(pre, -s + art[i].first);
    s += art[i].second;
    ans = max(ans, pre + s - art[i].first);
  }
  printf("%lld\n", ans);

#ifndef ONLINE_JUDGE
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

T3 团子制作

只需要关注哪些 G 字符之间有冲突,受团子的方向限制,它们必然只能是在一条斜线上的,对每条斜线上的 G G 的选择进行 DP,设 f ( i , j { 0 , v e r t i c a l , h o r i z o n a l } ) f(i, j \in \{0, vertical, horizonal\}) 表示这个 G 串成的团子的方向。 Θ ( n m ) \Theta(nm)

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

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

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

const int N = 3010;

int n, m, ans, cnt;
char s[N][N];
bool ab[N][2];
int dp[N][3];

void prog() {
  for (int i = 1; i <= cnt + 1; ++i) {
    dp[i][2] = *max_element(dp[i - 1], dp[i - 1] + 3);
    dp[i][0] = ab[i][0] ? (max(dp[i - 1][0], dp[i - 1][2]) + 1) : 0;
    dp[i][1] = ab[i][1] ? (max(dp[i - 1][1], dp[i - 1][2]) + 1) : 0;
  }
  ans += dp[cnt + 1][2];
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i)
    scanf("%s", s[i] + 1);
  for (int k = 2; k <= n + m; ++k) {
    for (int i = 1; i <= n; ++i) {
      int j = k - i;
      if (j < 1 || j > m)
        continue;
      if (s[i][j] != 'G') {
        if (cnt)
          prog();
        cnt = 0;
      } else {
        ++cnt;
        ab[cnt][0] = s[i - 1][j] == 'R' && s[i + 1][j] == 'W';
        ab[cnt][1] = s[i][j - 1] == 'R' && s[i][j + 1] == 'W';
      }
    }
    if (cnt)
      prog();
    cnt = 0;
  }
  printf("%d\n", ans);

#ifndef ONLINE_JUDGE
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

T4 月票购买

预处理出 u , v u,v 到每一个点的距离。在 s t s\rightarrow t 的最短路 DAG 上做一个 DP。还有一种情况是不借助月票,两种方案取较小值。主要就是跑 3 个单源最短路。 Θ ( m log m ) \Theta(m\log m)

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

#include <algorithm>
#include <functional>
#include <queue>
#include <set>
#include <map>
#include <vector>
#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;
  ll step;

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

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

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

const int N = 100010, M = 200010;

int n, m;
int s, t, a, b;
ll ans;
Edge* g[N];
bool vis[N];
ll diss[N], disa[N], disb[N], da[N], db[N];

void adde(int u, int v, int w);
void runspt(int s, ll* dis);
void dfs(int u);

int main() {
#ifndef ONLINE_JUDGE
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d%d%d%d%d%d", &n, &m, &s, &t, &a, &b);
  while (m--) {
    int u, v, w;
    scanf("%d%d%d", &u, &v, &w);
    adde(u, v, w);
    adde(v, u, w);
  }
  runspt(a, disa);
  ans = disa[b];
  if (disa[s] != -1) {
    runspt(b, disb);
    runspt(s, diss);
    dfs(t);
  }
  printf("%lld\n", ans);

#ifndef ONLINE_JUDGE
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

void dfs(int u) {
  if (vis[u])
    return;
  vis[u] = true;
  da[u] = disa[u];
  db[u] = disb[u];
  for (Edge* p = g[u]; p; p = p->next)
    if (diss[p->v] + p->w == diss[u]) {
      dfs(p->v);
      da[u] = min(da[u], da[p->v]);
      db[u] = min(db[u], db[p->v]);
    }
  ans = min(ans, min(da[u] + disb[u], db[u] + disa[u]));
}

void runspt(int s, ll* dis) {
  memset(dis, -1, sizeof(ll) * (n + 1));
  priority_queue<Node, vector<Node>, greater<Node>> q;
  q.emplace(s, dis[s] = 0);
  while (!q.empty()) {
    Node tmp = q.top();
    q.pop();
    if (tmp.step != dis[tmp.u])
      continue;
    for (Edge* p = g[tmp.u]; p; p = p->next)
      if (dis[p->v] == -1 || dis[p->v] > tmp.step + p->w)
        q.emplace(p->v, dis[p->v] = tmp.step + p->w);
  }
}

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

T5 毒蛇越狱

将位分值成两部分,前 A A 位和后 L A L - A 位。
对于询问中的前 A A 位里的问号,枚举其可能的情况,对于后面的 L A L - A 位,处理出所有可能的询问的答案。
复杂度 Θ ( 2 A ( Q + 3 L A ) ) \Theta(2^A(Q + 3^{L-A})) ,当 Q = 3 L A Q = 3^{L-A} 时,即取 A = L log 3 Q A = L - \log_3Q 时复杂度约为 Θ ( Q 0.37 2 L ) \Theta(Q^{0.37} \cdot 2^L)

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

#include <algorithm>
#include <functional>
#include <set>
#include <map>
#include <vector>
#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 IO_Tp
{
  const static int _O_Buffer_Size = 10 << 20;
  char _O_Buffer[_O_Buffer_Size], *_O_pos = _O_Buffer;

  IO_Tp()
  {
  }

  ~IO_Tp()
  {
    fwrite(_O_Buffer, 1, _O_pos - _O_Buffer, stdout);
  }

  IO_Tp &operator<<(int n)
  {
    static char _buf[10];
    char* _pos = _buf;
    do
      *_pos++ = '0' + n % 10;
    while (n /= 10);
    while (_pos != _buf)
      *_O_pos++ = *--_pos;
    return *this;
  }

  IO_Tp &operator<<(char ch)
  {
    *_O_pos++ = ch;
    return *this;
  }
} IO;

const int N = 1000010, L = 20;

int l, n;
char v[(1 << L) + 10];
char tmp[L + 4];
unsigned short sum[1 << 12][1 << 12];
int ind[N], cc[N];
int ans[N];

int main() {
#ifndef ONLINE_JUDGE
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif

  scanf("%d%d%s", &l, &n, v);
  for (int i = 1; i <= n; ++i) {
    scanf("%s", tmp + 1);
    for (int j = 0; j < l; ++j)
      if (tmp[l - j] == '?')
        cc[i] |= 1 << j;
      else
        ind[i] |= (tmp[l - j] - '0') << j;
  }
  int a = max(0, l - int(log(n) / log(3))), b = l - a;
  if (a > 12) {
    a = 12;
    b = l - a;
  } else if (b > 12) {
    b = 12;
    a = l - b;
  }
  for (int i = 0; i != (1 << l); ++i)
    v[i] -= '0';
  for (int pre = 0; pre != 1 << a; ++pre) {
    for (int s = 0; s != 1 << b; ++s)
      sum[0][s] = v[pre | s << a];
    for (int cov = 1; cov != 1 << b; ++cov) {
      int tr = __builtin_ctz(cov);
      int ccov = ~cov & ((1 << b) - 1);
      for (int s = ccov; ; s = (s - 1) & ccov) {
        sum[cov][s] = sum[cov ^ 1 << tr][s] + sum[cov ^ 1 << tr][s | 1 << tr];
        if (!s)
          break;
      }
    }

    for (int i = 1; i <= n; ++i)
      if ((~cc[i] & pre) == (ind[i] & ((1 << a) - 1)))
        ans[i] += sum[cc[i] >> a][ind[i] >> a];

    for (int cov = 0; cov != 1 << b; ++cov)
      for (int s = cov; ; s = (s - 1) & cov) {
        sum[~cov & ((1 << b) - 1)][s & cov] = 0;
        if (!s)
          break;
      }
  }

  for (int i = 1; i <= n; ++i)
    IO << ans[i] << '\n';

#ifndef ONLINE_JUDGE
  LOG("Time: %dms\n", int ((clock()
          -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

猜你喜欢

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