CodeForces Gym 101821 简要题解

Smart Vending

注意到策略其实是没有用的,所以需要想一种办法加速模拟。

注意到纸币价格只有 10 6 ,首先用硬币能买就买,之后的操作中,尽量多用纸币,这样可以将硬币数量控制在 10 6 以内,不断模拟下去一定会形成环,加上记忆化就可以了。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 1000005;
const int base = 1000000;

int b, c, r, tot, mem_b[N];
LL ans, mem_ans[N];
bool mem[N];

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(b), Read(c), Read(r), Read(tot), tot += c;
  ans += c / r, c %= r;
  int c_small = c % base, c_large = c / base, r_small = r % base, r_large = r / base, b_need = r_large - c_large;
  if (c_small < r_small) {
    if (b >= b_need + 1 && tot - c_small >= base - r_small) {
      b -= b_need + 1, ++ans, c = c_small - r_small + base;
    } else {
      printf("%lld\n", ans);
      return 0;
    }
  } else {
    if (b >= b_need) {
      b -= b_need, ++ans, c = c_small - r_small;
    } else {
      printf("%lld\n", ans);
      return 0;
    }
  }
  while (true) {
    if (mem[c]) {
      break;
    }
    mem[c] = true, mem_b[c] = b, mem_ans[c] = ans;
    if (c < r_small) {
      if (b >= r_large + 1 && tot - c >= base - r_small) {
        b -= r_large + 1, ++ans, c += base - r_small;
      } else {
        printf("%lld\n", ans);
        return 0;
      }
    } else {
      if (b >= r_large) {
        b -= r_large, ++ans, c -= r_small;
      } else {
        printf("%lld\n", ans);
        return 0;
      }
    }
  }
  LL t = b / (mem_b[c] - b);
  ans += t * (ans - mem_ans[c]), b %= mem_b[c] - b;
  while (true) {
    if (c < r_small) {
      if (b >= r_large + 1 && tot - c >= base - r_small) {
        b -= r_large + 1, ++ans, c += base - r_small;
      } else {
        printf("%lld\n", ans);
        return 0;
      }
    } else {
      if (b >= r_large) {
        b -= r_large, ++ans, c -= r_small;
      } else {
        printf("%lld\n", ans);
        return 0;
      }
    }
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

LIS vs. LDS

一个性质是 LIS 和 LDS 交集的大小至多为 1

f i 表示经过 i 的 LDS 个数, a l l 为总的 LDS 个数。考虑不合法的 LIS 长什么样,记它为 i 1 , i 2 , , i k ,那么所有 LDS 一定与它有交集,又因为交集不超过 1 ,所以交集恰好为 1 ,即 f i 1 + f i 2 + + f i k = a l l 。考虑如何求这样一个 LIS ,首先可以放到模一个大质数意义下考虑,那么用树状数组求 LIS 的同时,也维护一下当前 f 之和,维护两个不同的值就可以了,因为一个不合法必然另一个合法。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 500005;
const int mod = 1004535809;

struct Info {
  int val, mod_a, mod_b, pre_a, pre_b;

  Info(int val = 0, int mod_a = -1, int mod_b = -1, int pre_a = 0, int pre_b = 0):val(val), mod_a(mod_a), mod_b(mod_b), pre_a(pre_a), pre_b(pre_b) {}
} h[N];

int n, lds, lis, tot, a[N], w[N], seq[N];
pii f[N], g[N];
bool ban[N];

inline Info Merge(Info a, Info b) {
  if (a.val > b.val) {
    return a;
  }
  if (a.val < b.val) {
    return b;
  }
  if (!~a.mod_a) {
    return b;
  }
  if (!~a.mod_b) {
    if (~b.mod_a && b.mod_a != a.mod_a) {
      a.mod_b = b.mod_a, a.pre_b = b.pre_a;
    } else {
      a.mod_b = b.mod_b, a.pre_b = b.pre_b;
    }
  }
  return a;
}

namespace BITA {
int b[N], c[N];

inline void Clear() {
  for (int i = 1; i <= n; ++i) {
    b[i] = c[i] = 0;
  }
}

inline void Modify(int x, int v, int w) {
  for (; x <= n; x += x & -x) {
    if (CheckMax(b[x], v)) {
      c[x] = 0;
    }
    if (b[x] == v) {
      c[x] = (c[x] + w) % mod;
    }
  }
}

inline pii Query(int x) {
  int v = 0, w = 1;
  for (; x; x -= x & -x) {
    if (CheckMax(v, b[x])) {
      w = 0;
    }
    if (v == b[x]) {
      w = (w + c[x]) % mod;
    }
  }
  return mp(v, w);
}
}

namespace BITB {
Info b[N];

inline void Modify(int x, Info v) {
  for (; x <= n; x += x & -x) {
    b[x] = Merge(b[x], v);
  }
}

inline Info Query(int x) {
  Info v = Info(0, 0, -1, 0, 0);
  for (; x; x -= x & -x) {
    v = Merge(v, b[x]);
  }
  return v;
}
}

namespace BITC {
pii b[N];

inline void Modify(int x, pii v) {
  for (; x <= n; x += x & -x) {
    CheckMax(b[x], v);
  }
}

inline pii Query(int x) {
  pii v = mp(0, 0);
  for (; x; x -= x & -x) {
    CheckMax(v, b[x]);
  }
  return v;
}
}

inline void Output(int x, int y) {
  while (x) {
    seq[++tot] = x;
    if (h[x].mod_a == y) {
      y = (y - w[x] + mod) % mod;
      x = h[x].pre_a;
    } else {
      y = (y - w[x] + mod) % mod;
      x = h[x].pre_b;
    }
  }
  reverse(seq + 1, seq + tot + 1);
  printf("%d\n", lis);
  for (int i = 1; i <= tot; ++i) {
    printf("%d%c", seq[i], i == tot ? '\n' : ' ');
    ban[seq[i]] = true;
  }
  printf("%d\n", lds);
  for (int i = 1; i <= n; ++i) {
    if (!ban[i]) {
      f[i] = BITC::Query(n - a[i]);
      BITC::Modify(n - a[i] + 1, mp(++f[i].X, i));
      if (f[i].X == lds) {
        tot = 0;
        for (int x = i; x; seq[++tot] = x, x = f[x].Y);
        reverse(seq + 1, seq + tot + 1);
        for (int j = 1; j <= tot; ++j) {
          printf("%d%c", seq[j], j == tot ? '\n' : ' ');
        }
        return ;
      }
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n);
  for (int i = 1; i <= n; ++i) {
    Read(a[i]);
  }
  BITA::Clear();
  for (int i = 1; i <= n; ++i) {
    f[i] = BITA::Query(n - a[i]);
    BITA::Modify(n - a[i] + 1, ++f[i].X, f[i].Y);
    CheckMax(lds, f[i].X);
  }
  BITA::Clear();
  for (int i = n; i; --i) {
    g[i] = BITA::Query(a[i] - 1);
    BITA::Modify(a[i], ++g[i].X, g[i].Y);
  }
  int sum = 0;
  for (int i = 1; i <= n; ++i) {
    if (f[i].X + g[i].X - 1 == lds) {
      w[i] = 1LL * f[i].Y * g[i].Y % mod;
    } else {
      w[i] = 0;
    }
    if (f[i].X == lds) {
      sum = (sum + f[i].Y) % mod;
    }
  }
  BITA::Clear();
  for (int i = 1; i <= n; ++i) {
    f[i] = BITA::Query(a[i] - 1);
    BITA::Modify(a[i], ++f[i].X, f[i].Y);
    CheckMax(lis, f[i].X);
  }
  for (int i = 1; i <= n; ++i) {
    h[i] = BITB::Query(a[i] - 1);
    ++h[i].val;
    if (~h[i].mod_a) {
      h[i].mod_a = (h[i].mod_a + w[i]) % mod;
    }
    if (~h[i].mod_b) {
      h[i].mod_b = (h[i].mod_b + w[i]) % mod;
    }
    if (h[i].val == lis) {
      if (~h[i].mod_a && h[i].mod_a != sum) {
        Output(i, h[i].mod_a);
        return 0;
      }
      if (~h[i].mod_b && h[i].mod_b != sum) {
        Output(i, h[i].mod_b);
        return 0;
      }
    }
    BITB::Modify(a[i], Info(h[i].val, h[i].mod_a, h[i].mod_b, ~h[i].mod_a ? i : 0, ~h[i].mod_b ? i : 0));
  }
  Debug("%d %d\n", lis, lds);
  puts("IMPOSSIBLE");
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

Eat And Walk

显然最优策略是走到最右端的某个点,在回来的路上吃某些餐馆。

f i 表示吃了 i 单位食物时,最少需要消耗的能量。考虑倒着转移,不难发现只考虑 i , i + 1 , , n f 的取值至多取 e i ,因为每单位食物都会带来至少 i 的代价,而走到最右端的某个点只需要在处理 f 0 转移出去的时候加上代价就可以了。复杂度是调和级数。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 10000005;

int n, m, a[N], f[N];

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m);
  for (int i = 1; i <= m; ++i) {
    f[i] = m + 1;
  }
  for (int i = 1; i <= n; ++i) {
    Read(a[i]);
  }
  for (int i = n; i; --i) {
    for (int j = m / i; j >= a[i]; --j) {
      if (j == a[i]) {
        CheckMin(f[j], (i << 1) + a[i] * i);
      } else {
        CheckMin(f[j], f[j - a[i]] + a[i] * i);
      }
    }
  }
  for (int i = m; ~i; --i) {
    if (f[i] <= m) {
      printf("%d\n", i);
      return 0;
    }
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

Search Engine

建出后缀自动机,考虑往前加字符就是在 p a r e n t 树上不动或者走向某个儿子,而往后加字符就是走转移边。注意到离开当前节点的时候,出现次数不会变多,那么在当前节点待到最后一定是不劣的,然后做个DP就可以了。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 400005;

int n, lst, tot, deg[N], par[N], siz[N], val[N], nxt[N][26];
vector <int> adj[N];
queue <int> q;
LL ans, f[N];
char s[N];

inline void Extend(int w) {
  int p = lst, np = ++tot;
  val[np] = val[p] + 1, siz[np] = 1;
  for (; p && !nxt[p][w]; nxt[p][w] = np, p = par[p]);
  if (!p) {
    par[np] = 1;
  } else {
    int q = nxt[p][w];
    if (val[q] == val[p] + 1) {
      par[np] = q;
    } else {
      int nq = ++tot;
      val[nq] = val[p] + 1;
      memcpy(nxt[nq], nxt[q], sizeof nxt[nq]);
      par[nq] = par[q], par[np] = par[q] = nq;
      for (; p && nxt[p][w] == q; nxt[p][w] = nq, p = par[p]);
    }
  }
  lst = np;
}

inline void DFS(int x) {
  for (auto y : adj[x]) {
    DFS(y), siz[x] += siz[y];
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), scanf("%s", s + 1), lst = tot = 1;
  for (int i = 1; i <= n; ++i) {
    Extend(s[i] - 'a');
  }
  for (int i = 2; i <= tot; ++i) {
    adj[par[i]].pb(i), ++deg[i];
  }
  DFS(1);
  for (int i = 1; i <= tot; ++i) {
    for (int j = 0; j < 26; ++j) {
      if (nxt[i][j]) {
        adj[i].pb(nxt[i][j]), ++deg[nxt[i][j]];
      }
    }
  }
  for (q.push(1); !q.empty(); q.pop()) {
    int x = q.front();
    for (auto y : adj[x]) {
      CheckMax(f[y], f[x] + 1LL * (val[y] - val[x]) * siz[y]);
      if (!--deg[y]) {
        q.push(y);
      }
    }
    CheckMax(ans, f[x]);
  }
  printf("%lld\n", ans);
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

Guess Me If You Can

考虑随机一个排列 q ,然后依次让 p q i 1 ,不难发现如果当前不同数个数变少了那 q i 一定不是 n 。考虑一个不是 n 的数 x 什么情况下会被排除:当且仅当在排列 q 中它在 x 1 x + 1 前面。正确率为 ( 1 ( 2 3 ) 50 ) 1000 ,约为 1 10 6 ,可以通过。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 1005;

int n, m, cur, nxt, p[N];
bool v[N];

int main() {
  srand(time(0));
  Read(n), cur = m = n;
  for (int i = 1; i <= n; ++i) {
    p[i] = i;
  }
  while (m > 1) {
    random_shuffle(p + 1, p + n + 1);
    for (int i = 1; i <= n; ++i) {
      printf("0 %d\n", p[i]);
      fflush(stdout);
      Read(nxt);
      if (nxt < cur) {
        if (!v[p[i]]) {
          v[p[i]] = true, --m;
        }
      }
      cur = nxt;
    }
  }
  for (int i = 1; i <= n; ++i) {
    if (!v[i]) {
      printf("1 %d\n", i);
      fflush(stdout);
      return 0;
    }
  }
  return 0;
}

Lazy Hash Table

两个数不冲突当且仅当模数不是它们差的约数,做一次 FFT 求出所有可能的差,然后枚举答案即可。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 4194305;
const int M = 25;
const double pi = acos(-1);

struct E {
  double x, y;

  E(double x = 0, double y = 0):x(x), y(y) {}

  E operator + (const E &b) const {
    return E(x + b.x, y + b.y);
  }

  E operator - (const E &b) const {
    return E(x - b.x, y - b.y);
  }

  E operator * (const E &b) const {
    return E(x * b.x - y * b.y, x * b.y + y * b.x);
  }
} I[N], W[N], X[N], Y[N];

int D, L, R[N];

inline void Ini(int n) {
  for (D = 1, L = 0; D < n; D <<= 1, ++L);
  for (int i = 1; i < D; ++i) {
    R[i] = (R[i >> 1] >> 1) | ((i & 1) << L - 1);
  }
  for (int i = 0; i < D; ++i) {
    W[i] = I[i] = E(cos(2 * pi * i / D), sin(2 * pi * i / D)), I[i].y = -I[i].y;
  }
}

inline void DFT(E *X, E *W) {
  for (int i = 0; i < D; ++i) {
    if (i < R[i]) {
      swap(X[i], X[R[i]]);
    }
  }
  for (int i = 1, l = L - 1; i < D; i <<= 1, --l) {
    for (int j = 0; j < D; j += i << 1) {
      for (int k = 0; k < i; ++k) {
        E u = X[j + k], v = X[j + k + i] * W[k * (1 << l)];
        X[j + k] = u + v, X[j + k + i] = u - v;
      }
    }
  }
}

inline vector <int> Mul(vector <int> a, vector <int> b) {
  int n = a.size(), m = b.size();
  vector <int> c(n + m - 1, 0);
  Ini(n + m - 1);
  for (int i = 0; i < D; ++i) {
    X[i] = i < n ? E(a[i], 0) : E(0, 0);
    Y[i] = i < m ? E(b[i], 0) : E(0, 0);
  }
  DFT(X, W), DFT(Y, W);
  for (int i = 0; i < D; ++i) {
    X[i] = X[i] * Y[i];
  }
  DFT(X, I);
  for (int i = 0; i < n + m - 1; ++i) {
    c[i] = X[i].x / D + 0.5;
  }
  return c;
}

int n, m, a[N], b[N];

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n);
  for (int i = 1, x; i <= n; ++i) {
    Read(x), CheckMax(m, x), a[x] = 1;
  }
  for (int i = 1; i <= m; ++i) {
    if (a[i]) {
      b[m - i] = 1;
    }
  }
  vector <int> c = Mul(vector <int> (a, a + m + 1), vector <int> (b, b + m + 1));
  for (int i = 1; i <= m; ++i) {
    if (c[i + m]) {
      a[i] = 1;
    } else {
      a[i] = 0;
    }
  }
  for (int i = 1; i <= m; ++i) {
    bool f = false;
    for (int j = i; j <= m; j += i) {
      f |= a[j];
    }
    if (!f) {
      printf("%d\n", i);
      return 0;
    }
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

猜你喜欢

转载自blog.csdn.net/wxh010910/article/details/80646350