SNOI2019 简要题解

D1T1 字符串

dp 出所有相邻两个位置的 lcp,然后即可优化 cmp。 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int N = 1000010;

int n;
int lcp[N], v[N], rk[N];
char s[N];


bool cmp(int x, int y) {
  if (x < y) {
    int l = lcp[x];
    if (l >= y - x)
      return false;
    return s[x + l + 1] < s[x + l];
  }
  int l = lcp[y];
  if (l >= x - y)
    return false;
  return s[y + l] < s[y + l + 1];
}

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

  scanf("%d%s", &n, s + 1);
  for (int i = n - 1; i; --i)
    lcp[i] = (s[i] != s[i + 1]) ? 0 : (1 + lcp[i + 1]);
  for (int i = 1; i <= n; ++i)
    v[i] = i;
  stable_sort(v + 1, v + n + 1, cmp);
  for (int i = 1; i <= n; ++i)
    rk[v[i]] = i;
  for (int i = 1; i <= n; ++i)
    printf("%d ", v[i]);

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

D1T2 数论

先把所有不同的 gcd ⁡ ( P , Q ) \gcd(P,Q) gcd(P,Q) 剩余类分开处理,根据 CRT 可以发现将每个数可以乘以那个转换到   m o d   P ′ Q ′ \bmod P'Q' modPQ 的定值,sort 后扫一下即可。 Θ ( n log ⁡ n ) \Theta(n\log n) Θ(nlogn)

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int N = 1000010;

int p, q, n, m, rp, rq, g;
bool a[N], b[N];
ll ca[N];
ll t;

int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }

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 inv(int a, int p) {
  int x, y;
  exGcd(a, p, x, y);
  return x < 0 ? x + p : x;
}

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

  scanf("%d%d%d%d%lld", &p, &q, &n, &m, &t);
  while (n--) {
    int x;
    scanf("%d", &x);
    a[x] = true;
  }
  while (m--) {
    int x;
    scanf("%d", &x);
    b[x] = true;
  }
  g = gcd(p, q);
  rp = p / g;
  rq = q / g;
  ll ans = 0;
  ll m = rp * (ll)rq;
  ll ip = inv(rq, rp) * (ll)rq, iq = inv(rp, rq) * (ll)rp;
  for (int r = 0; r < g && r < t; ++r) {
    int cnta = 0, cntb = 0;
    for (int i = 0; i < rp; ++i)
      if (a[i * g + r])
        ca[++cnta] = i * (ll)ip % m;
    for (int i = 0; i < rq; ++i)
      cntb += b[i * g + r];
    ll tt = (t - r + g - 1) / g;
    ans += tt / m * cnta * cntb;
    tt %= m;
    sort(ca + 1, ca + cnta + 1);
    for (int i = 0; i < rq; ++i)
      if (b[i * g + r]) {
        ll cur = i * (ll)iq % m;
        ans += lower_bound(ca + 1, ca + cnta + 1, tt - cur) - ca - 1;
        ans += lower_bound(ca + 1, ca + cnta + 1, m + tt - cur) - lower_bound(ca + 1, ca + cnta + 1, m - cur);
      }
  }
  printf("%lld\n", ans);

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

D1T3 通信

主席树优化建图费用流。复杂度为什么对啊?好像是 Θ ( n 3 log ⁡ 2 n ) \Theta(n^3\log^2 n) Θ(n3log2n) 吧?

#include 
#include 
#include 
#include 

#include 

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

using namespace std;

typedef long long ll;

const int N = 1010, M = 50010;

int n, vc, S, T, w;
int a[N], ver[N], vero[N];
bool inq[M], vis[M], good[M];
int que[M];
ll dis[M];
pair<int, int> p[N];

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

E* pth[M];
E* g[M];

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

void link(int u, int v, int w, int c) {
  if (u == 0 || v == 0) return;
  E *p = adde(u, v, w, c), *q = adde(v, u, 0, -c);
  p->rev = q;
  q->rev = p;
}

struct Node {
  int l, r, id;
  Node *ls, *rs;

  void lk(int u, int k, int w) {
    if (k == r) {
      link(id, u, 1, w);
      return;
    }
    if (k <= ls->r)
      ls->lk(u, k, w);
    else {
      link(ls->id, u, M, w);
      rs->lk(u, k, w);
    }
  }
};

Node* newNode() {
  static Node pool[M], *top = pool;
  return top++;
}

Node* build(int l, int r) {
  Node* p = newNode();
  p->l = l;
  p->r = r;
  if (l == r)
    return p;
  int mid = (l + r) >> 1;
  p->ls = build(l, mid);
  p->rs = build(mid + 1, r);
  return p;
}

int newVertex() {
  return ++vc;
}

void ins(Node* o, int k, int sgn) {
  if (o->l == o->r) {
    o->id = newVertex();
    link(vero[k], o->id, 1, a[k] * sgn - w);
    return;
  }
  Node* p = (k <= o->ls->r) ? o->ls : o->rs;
  ins(p, k, sgn);
  o->id = newVertex();
  link(o->ls->id, o->id, o->ls->r - o->ls->l + 1, 0);
  link(o->rs->id, o->id, o->rs->r - o->rs->l + 1, 0);
}

ll augment() {
  LOG("AUGMENT %d\n", vc);
  int ql = 0, qr = 0;

  que[qr++] = T;
  memset(good, 0, sizeof(good));
  good[T] = true;
  while (ql != qr) {
    int u = que[ql++];
    for (E* p = g[u]; p; p = p->next)
      if (p->rev->w && !good[p->v]) {
        good[p->v] = true;
        que[qr++] = p->v;
      }
  }

  ql = 0;
  qr = 0;
  memset(vis, 0, sizeof(vis));
  que[qr++] = S;
  inq[S] = true;
  dis[S] = 0;
  vis[S] = true;
  pth[S] = NULL;
  while (ql != qr) {
    int u = que[ql++];
    if (ql == M) ql = 0;
    inq[u] = false;
    for (E* p = g[u]; p; p = p->next)
      if (p->w && good[p->v] && (!vis[p->v] || (dis[p->v] > dis[u] + p->c))) {
        vis[p->v] = true;
        dis[p->v] = dis[u] + p->c;
        pth[p->v] = p->rev;
        if (!inq[p->v]) {
          que[qr++] = p->v;
          if (qr == M) qr = 0;
          inq[p->v] = true;
        }
      }
  }
  if (!vis[T]) {
    return 1;
  }
  ll ret = dis[T];
  E* p = pth[T];
  while (p) {
    ++p->w;
    --p->rev->w;
    p = pth[p->v];
  }
  return ret;
}

int main() {
  scanf("%d%d", &n, &w);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &a[i]);
  for (int i = 1; i <= n; ++i)
    p[i] = make_pair(a[i], i);
  sort(p + 1, p + n + 1);
  S = newVertex();
  T = newVertex();
  ll ans = n * (ll)w;
  for (int i = 1; i <= n; ++i) {
    ver[i] = newVertex();
    vero[i] = newVertex();
    link(S, ver[i], 1, 0);
    link(ver[i], vero[i], 1, 0);
    link(vero[i], T, 1, 0);
  }
  Node *le = build(1, n), *ge = build(1, n);
  for (int i = 1; i <= n; ++i) {
    int j = p[i].second;
    le->lk(ver[j], j, a[j]);
    ins(le, j, -1);
  }
  for (int i = n; i; --i) {
    int j = p[i].second;
    ge->lk(ver[j], j, -a[j]);
    ins(ge, j, 1);
  }
  ll cost;
  while ((cost = augment()) < 0)
    ans += cost;
  printf("%lld\n", ans);
  return 0;
}

D2T1 纸牌

某种意义上,显然应当先竖着拿。于是可以记状态 s = ( x , y ) s=(x,y) s=(x,y) 表示当前以及下一个这个位置至少支付几个牌用于偿还前面的横牌。矩阵乘法优化转移。 Θ ( X s 6 log ⁡ n ) \Theta(X s^6\log n) Θ(Xs6logn),其中 s = 3 s=3 s=3

#include 
#include 

using namespace std;

typedef long long ll;

const int N = 1010, L = 60, P = 998244353, S = 15;

int st[L][S][S], tran[N][S][S];
int dp[S];
ll k[N];
int a[N];

ll n;
int c, m;

void trans(int (*mat)[S]) {
  static int tmp[S];
  memset(tmp, 0, sizeof(tmp));
  for (int i = 0; i < S; ++i)
    for (int j = 0; j < S; ++j)
      tmp[j] = (tmp[j] + dp[i] * (ll)mat[i][j]) % P;
  memcpy(dp, tmp, sizeof(dp));
}

int fdiv(int a, int b) {
  if (a < 0)
    return (a - b + 1) / b;
  return a / b;
}

int main() {
  scanf("%lld%d%d", &n, &c, &m);
  for (int i = 1; i <= m; ++i)
    scanf("%lld%d", &k[i], &a[i]);
  k[m + 1] = n + 1;
  for (int i = 0; i <= c; ++i) {
    for (int j = 0; j <= 4; ++j)
      for (int k = 0; k <= 2; ++k) {
        for (int ad = 0; ad <= 2; ++ad) {
          if (ad + j > c) break;
          if (ad + j > i)
            tran[i][j * 3 + k][(k + ad) * 3 + ad] = ((c - ad - j) / 3 + 1) % P;
          else {
            int r = (ad + j) % 3;
            tran[i][j * 3 + k][(k + ad) * 3 + ad] = ((c - r) / 3 - fdiv(i - r - 1, 3)) % P;
          }
        }
      }
  }
  memcpy(st[0], tran[0], sizeof(st[0]));
  for (int i = 1; i < L; ++i) {
    for (int j = 0; j < S; ++j)
      for (int k = 0; k < S; ++k)
        for (int l = 0; l < S; ++l)
          st[i][j][k] = (st[i][j][k] + st[i - 1][j][l] * (ll)st[i - 1][l][k]) % P;
  }
  dp[0] = 1;
  for (int i = 1; i <= m; ++i) {
    for (int b = 0; b < L; ++b)
      if ((k[i] - k[i - 1] - 1) >> b & 1)
        trans(st[b]);
    trans(tran[a[i]]);
  }
  for (int b = 0; b < L; ++b)
    if ((n - k[m]) >> b & 1)
      trans(st[b]);
  printf("%d\n", dp[0]);
  return 0;
}

D2T2 积木

考虑把两个图叠到一起看,就是要消去若干个圈。构造欧拉回路遍历整个棋盘即可。 Θ ( n 2 ) \Theta(n^2) Θ(n2)

#include 
#include 

#include 

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

using namespace std;

const int N = 2010;

int n, m, cnt;
char s[N][N], t[N][N];
const char* v = "LURD";
int dx[] = {0, -1, 0, 1}, dy[] = {-1, 0, 1, 0};
int mp[256];
bool vis[N][N];
int vis2[N * N];
char ans[N * N * 2];
int gt[N * N], rvs[N * N];
char w[N * N];

void adde(int u, int v, char ww) {
  gt[u] = v;
  w[u] = ww;
  rvs[v] = u;
}

int gid(int x, int y) { return (x - 1) * m + y; }

void dfs(int u) {
  vis2[u] = 1;
  if (gt[u] && vis2[gt[u]] != -1) {
    if (!vis2[gt[u]])
      dfs(gt[u]);
    ans[++cnt] = w[u];
  }
  int vx = (u - 1) / m + 1, vy = (u - 1) % m + 1;
  for (int dir = 0; dir < 4; ++dir) {
    int x = vx + dx[dir], y = vy + dy[dir];
    if (x < 1 || x > n || y < 1 || y > m)
      continue;
    if (s[x][y] == 'o')
      continue;
    int dd = mp[s[x][y]];
    int gx = x + dx[dd], gy = y + dy[dd];
    if (vis2[gid(gx, gy)])
      continue;
    int cyc = rvs[gid(gx, gy)];
    if (cyc == 0) {
      ans[++cnt] = v[dd ^ 2];
    }
    dfs(gid(gx, gy));
    ans[++cnt] = v[dir];
  }
  vis2[u] = -1;
}

int main() {
  mp['u'] = 1;
  mp['n'] = 3;
  mp['<'] = 2;
  mp['>'] = 0;

  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; ++i)
    scanf("%s", s[i] + 1);
  for (int i = 1; i <= n; ++i)
    scanf("%s", t[i] + 1);
  int px, py;
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      if (s[i][j] == 'o') {
        px = i;
        py = j;
      }
  vis[px][py] = true;
  int x = px, y = py;
  while (t[x][y] != 'o') {
    int dir = mp[t[x][y]];
    int gx = x + dx[dir], gy = y + dy[dir];
    int dd = mp[s[gx][gy]];
    adde(gid(x, y), gid(gx + dx[dd], gy + dy[dd]), v[dir]);
    x = gx + dx[dd];
    y = gy + dy[dd];
    vis[x][y] = true;
  }
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j) {
      if (vis[i][j])
        continue;
      if (s[i][j] == t[i][j])
        continue;
      if ((i ^ j) & 1)
        continue;
      x = i;
      y = j;
      while (!vis[x][y]) {
        vis[x][y] = true;
        int dir = mp[t[x][y]];
        int gx = x + dx[dir], gy = y + dy[dir];
        int dd = mp[s[gx][gy]];
        adde(gid(x, y), gid(gx + dx[dd], gy + dy[dd]), v[dir]);
        x = gx + dx[dd];
        y = gy + dy[dd];
      }
    }
  dfs(gid(px, py));
  reverse(ans + 1, ans + cnt + 1);
  puts(ans + 1);
  return 0;
}

D2T3 网络

不可做啊?

你可能感兴趣的:(题集/比赛题解)