尝试使得每个点到某一祖先的路径都可以被一个矩形框住,发现类似析合树对应的置换撒点 ( 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]);
}
状压切面的连通性,预处理一下转移。看起来转移方法有 Θ ( 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;
}
枚举每个位置以其为山谷的最高值是不是无洞的,用并查集进行合并,通过平面欧拉公式判断是否有洞。时间复杂度 Θ ( h + n 2 α ( n ) ) \Theta(h + n^2\alpha(n)) Θ(h+n2α(n))