LCA题目选讲4
两道紫题,哭了
T1 [BZOJ 1977]次小生成树
我们可以使用 \(kruskal\) 来求出最小生成树,然后通过最小生成树来生成严格次小生成树。
可以证明存在一个严格次小生成树与最小生成树之有一条边的区别,我们枚举非树边加入到树中形成一个奇环树,我们再枚举环上的边断掉一条,我们为了让树边权和尽可能小,我们选择断掉最长的那条边,为了求严格次小生成树,假如最长边与非树边长度相同,则断掉严格次大边。
求环上的边权我们可以使用倍增来维护。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
int read() {
int a = 0, x = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-')
x = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
a = a * 10 + ch - '0';
ch = getchar();
}
return a * x;
}
const int N = 1e5 + 7, M = 1e6 + 7;
const ll inf = 1e18 + 7;
int n, m;
int f[N];
int find(int s) {
if (f[s] == s)
return s;
return f[s] = find(f[s]);
}
int head[N], go[M], nxt[M], val[M], cnt;
void add(int u, int v, int w) {
go[++cnt] = v;
nxt[cnt] = head[u];
head[u] = cnt;
val[cnt] = w;
}
int fa[N][31], g[N][31], h[N][31];
void upd(int &a, int &b, int c, int d) {
int ret1 = max(a, c), ret2 = max(b, d);
if (ret1 != a)
ret2 = max(ret2, a);
if (ret1 != c)
ret2 = max(ret2, c);
a = ret1, b = ret2;
}
int dis[N];
void LCA(int a, int b, int &mx1, int &mx2) {
if (dis[a] < dis[b])
swap(a, b);
for (int i = 30; i >= 0; i--) {
if (dis[fa[a][i]] >= dis[b]) {
upd(mx1, mx2, h[a][i], g[a][i]);
a = fa[a][i];
}
}
if (a == b)
return;
for (int i = 30; i >= 0; i--) {
if (fa[a][i] != fa[b][i]) {
upd(mx1, mx2, h[a][i], g[a][i]);
upd(mx1, mx2, h[b][i], g[b][i]);
a = fa[a][i], b = fa[b][i];
}
}
upd(mx1, mx2, h[a][0], g[a][0]);
upd(mx1, mx2, h[b][0], g[b][0]);
}
void dfs(int u) {
for (int i = 1; i <= 30; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
h[u][i] = h[u][i - 1], g[u][i] = g[u][i - 1];
upd(h[u][i], g[u][i], h[fa[u][i - 1]][i - 1], g[fa[u][i - 1]][i - 1]);
}
for (int e = head[u]; e; e = nxt[e]) {
int v = go[e];
if (v == fa[u][0])
continue;
fa[v][0] = u, dis[v] = dis[u] + 1;
h[v][0] = val[e];
dfs(v);
}
}
struct node {
int u, v, w;
friend bool operator<(node a, node b) { return a.w < b.w; }
} arr[M];
ll sum = 0;
bool vis[M];
int main() {
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
n = read(), m = read();
for (int i = 1; i <= m; i++) {
arr[i].u = read(), arr[i].v = read(), arr[i].w = read();
}
for (int i = 1; i <= n; i++) f[i] = i;
sort(arr + 1, arr + 1 + m);
for (int i = 1; i <= m; i++) {
if (find(arr[i].u) != find(arr[i].v)) {
sum += arr[i].w;
f[find(arr[i].u)] = find(arr[i].v);
vis[i] = 1;
add(arr[i].u, arr[i].v, arr[i].w);
add(arr[i].v, arr[i].u, arr[i].w);
}
}
dfs(1);
ll ans = inf;
for (int i = 1; i <= m; i++) {
if (vis[i])
continue;
int mx1(0), mx2(0);
LCA(arr[i].u, arr[i].v, mx1, mx2);
if (!mx1)
continue;
// printf("--%d %d--\n",mx1,mx2);
if (mx1 != arr[i].w) {
ans = min(ans, (ll)arr[i].w - mx1);
} else if (mx2)
ans = min(ans, (ll)arr[i].w - mx2);
}
printf("%lld", sum + ans);
return 0;
}
T2 [BZOJ 2306][CTSC 2011]幸福路径
正解是倍增Flody求解。 这和LCA什么关系啊?
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define double long double
using namespace std;
int read() {
int a = 0, x = 1;
char ch = getchar();
while (ch > '9' || ch < '0') {
if (ch == '-')
x = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
a = a * 10 + ch - '0';
ch = getchar();
}
return a * x;
}
const int N = 1e2 + 7, M = 1e6 + 7, inf = 2e9 + 7;
const double eps = 1e-12;
int n, m;
double w[N];
int s;
double p;
double dp[N][N], f[N][N];
int main() {
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
n = read(), m = read();
for (int i = 1; i <= n; i++) {
scanf("%Lf", &w[i]);
}
s = read();
scanf("%Lf", &p);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (i != j)
f[i][j] = -inf;
}
}
for (int i = 1; i <= m; i++) {
int a = read(), b = read();
f[a][b] = w[b] * p;
}
double tmp = p;
for (int h = 1; h <= 60; h++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = -inf;
}
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = max(dp[i][j], f[i][k] + p * f[k][j]);
// printf(" %d %d %d %Lf %Lf %Lf
//%Lf\n",i,k,j,dp[i][j],f[i][k],f[k][j],f[i][k]+p*f[k][j]);
}
}
}
p *= p;
// for(int i = 1;i <= n;i ++,putchar('\n')) {
// for(int j = 1;j <= n;j ++) {
// printf("dp[%d][%d] = %13.1Lf ",i,j,dp[i][j]);
// }
// }putchar('\n');
memcpy(f, dp, sizeof(dp));
if (p < eps)
break;
}
double ans = 0;
for (int i = 1; i <= n; i++) {
ans = max(ans, f[s][i]);
}
printf("%.1Lf", ans + w[s]);
return 0;
}