USACO OPEN 2019 Platinum 简要题解

T1 Tree Boxes

尝试使得每个点到某一祖先的路径都可以被一个矩形框住,发现类似析合树对应的置换撒点 ( i , p i ) (i,p_i) (i,pi) 就满足这一要求。

#include "grader.h"

#include 

struct E { int v; E* next; };

const int N = 100010, LGN = 19;

int n;
int posx[N], posy[N], dep[N], sub[N];
int prt[N][LGN];
E* g[N];

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

void addRoad(int A, int B) {
  adde(A, B);
  adde(B, A);
}

void dfs1(int u) {
  for (int i = 1; i < LGN; ++i)
    prt[u][i] = prt[prt[u][i - 1]][i - 1];
  sub[u] = 1;
  for (E* p = g[u]; p; p = p->next)
    if (!sub[p->v]) {
      dep[p->v] = dep[u] + 1;
      prt[p->v][0] = u;
      dfs1(p->v);
      sub[u] += sub[p->v];
    }
}

void dfs2(int u, int x, int y) {
  setFarmLocation(u, x, y);
  posx[u] = x;
  posy[u] = y;
  ++x;
  y += sub[u];
  for (E* p = g[u]; p; p = p->next)
    if (dep[p->v] == dep[u] + 1) {
      dfs2(p->v, x, y - sub[p->v]);
      x += sub[p->v];
      y -= sub[p->v];
    }
}

void buildFarms() {
  dfs1(0);
  dfs2(0, 1, 1);
}

void notifyFJ(int A, int B) {
  if (dep[A] > dep[B]) std::swap(A, B);
  int u = A, v = B;
  for (int i = LGN - 1; i >= 0; --i)
    if ((dep[v] - dep[u]) >> i & 1)
      v = prt[v][i];
  if (u == v) {
    addBox(posx[A], posy[A], posx[B], posy[B]);
    return;
  }
  for (int i = LGN - 1; i >= 0; --i)
    if (prt[u][i] != prt[v][i]) {
      u = prt[u][i];
      v = prt[v][i];
    }
  u = prt[u][0];
  addBox(posx[u], posy[u], posx[A], posy[A]);
  addBox(posx[v], posy[v], posx[B], posy[B]);
}

T2 Compound Escape

状压切面的连通性,预处理一下转移。看起来转移方法有 Θ ( 2 k C k ) \Theta(2^k C_k) Θ(2kCk) 种,实则经过预处理发现竖向边的转移总共只有 1414 种,横向边的转移只有 2112 种。复杂度 Θ ( n ⋅ # T r a n s ) \Theta(n\cdot \#Trans) Θ(n#Trans)

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

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

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> Ans;

const int N = 30000, K = 6, C = 132, HSH = 2000;
const int P = 1000000007;
const ll INF = 1LL << 60;
const Ans AINF = Ans(INF, 1);

int n, k, c;
int a[N][K], b[N][K];
int t1c[1 << (K - 1)], t2c[1 << K];
int tran1[1 << (K - 1)][C], tran1v[1 << (K - 1)][C], tran2[1 << K][C], tran2v[1 << K][C];
int cur[K], tmp[K], fl[K];
int difcc[HSH], rval[C];
Ans acur[C], atmp[C];

int norm(int x) { return (x >= P) ? (x - P) : x; }

Ans o(const Ans& lhs, const Ans& rhs) { return lhs.first == rhs.first ? Ans(lhs.first, norm(lhs.second + rhs.second)) : min(lhs, rhs); }

int find(int* f, int x) { return f[x] == x ? x : f[x] = find(f, f[x]); }

int toid(int* cc) {
  static int cur[K];
  memcpy(cur, cc, sizeof(cur));
  int x = 0;
  for (int i = 0; i < k; ++i) {
    cur[i] = cur[cur[i]];
    x = x * k + cur[i];
  }
  return x;
}

void tocc(int x, int* cc) {
  for (int i = k - 1; i >= 0; --i) {
    cc[i] = x % k;
    x /= k;
  }
}

void pre(int id) {
  static int p[K];
  if (id == k) {
    int hsh = toid(p);
    rval[c] = hsh;
    difcc[hsh] = c++;
    return;
  }
  for (p[id] = 0; p[id] <= id; ++p[id]) {
    if (p[p[id]] != p[id])
      continue;
    bool flag = false;
    for (int i = p[id] + 1; i < id; ++i)
      if (p[i] < p[id]) {
        flag = true;
        break;
      }
    if (!flag)
      pre(id + 1);
  }
}

void dfs1(int* arr, int id, int s, ll sum) {
  if (id == k - 1) {
    for (int i = 0; i < t1c[s]; ++i) {
      int u = tran1[s][i], v = tran1v[s][i];
      atmp[v] = o(atmp[v], Ans(acur[u].first + sum, acur[u].second));
    }
    return;
  }
  dfs1(arr, id + 1, s | 1 << id, sum + arr[id]);
  dfs1(arr, id + 1, s, sum);
}

void dfs2(int* arr, int id, int s, ll sum) {
  if (id == k) {
    for (int i = 0; i < t2c[s]; ++i) {
      int u = tran2[s][i], v = tran2v[s][i];
      atmp[v] = o(atmp[v], Ans(acur[u].first + sum, acur[u].second));
    }
    return;
  }
  dfs2(arr, id + 1, s | 1 << id, sum + arr[id]);
  dfs2(arr, id + 1, s, sum);
}

void dp1(int* arr) {
  fill(atmp, atmp + c, AINF);
  dfs1(arr, 0, 0, 0);
  memcpy(acur, atmp, sizeof(atmp));
}

void dp2(int* arr) {
  fill(atmp, atmp + c, AINF);
  dfs2(arr, 0, 0, 0);
  memcpy(acur, atmp, sizeof(atmp));
}

int main() {
  freopen("escape.in", "r", stdin);
  freopen("escape.out", "w", stdout);

  scanf("%d%d", &n, &k);
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < k - 1; ++j)
      scanf("%d", &a[i][j]);
  for (int j = 0; j < k; ++j)
    for (int i = 1; i < n; ++i)
      scanf("%d", &b[i][j]);
  memset(difcc, -1, sizeof(difcc));
  pre(0);
  for (int u = 0; u < c; ++u) {
    int key = rval[u];
    for (int s = 0; s < 1 << (k - 1); ++s) {
      tocc(key, cur);
      bool flag = false;
      for (int i = 1; i < k; ++i)
        if ((s >> (i - 1)) & 1) {
          int x = find(cur, i - 1), y = find(cur, i);
          if (x == y) {
            flag = true;
            break;
          } else {
            if (x > y)
              swap(x, y);
            cur[y] = x;
          }
        }
      if (!flag) {
        tran1[s][t1c[s]] = u;
        tran1v[s][t1c[s]++] = difcc[toid(cur)];
      }
    }
    tocc(key, cur);
    for (int s = 0; s < 1 << k; ++s) {
      memset(tmp, -1, sizeof(tmp));
      for (int i = 0; i < k; ++i)
        if ((s >> i) & 1) {
          if (tmp[cur[i]] == -1)
            tmp[cur[i]] = i;
          fl[i] = tmp[cur[i]];
        } else
          fl[i] = i;
      bool flag = false;
      for (int i = 0; i < k; ++i)
        if (tmp[cur[i]] == -1) {
          flag = true;
          break;
        }
      if (!flag) {
        tran2[s][t2c[s]] = u;
        tran2v[s][t2c[s]++] = difcc[toid(fl)];
      }
    }
  }
  fill(acur, acur + c, AINF);
  acur[c - 1] = Ans(0, 1);
  dp1(a[0]);
  for (int i = 1; i < n; ++i) {
    dp2(b[i]);
    dp1(a[i]);
  }
  printf("%d\n", acur[0].second);
  return 0;
}

T3 Valleys

枚举每个位置以其为山谷的最高值是不是无洞的,用并查集进行合并,通过平面欧拉公式判断是否有洞。时间复杂度 Θ ( h + n 2 α ( n ) ) \Theta(h + n^2\alpha(n)) Θ(h+n2α(n))

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