标签:最小生成树 ns2 Fix int cas set def false 链式
题目链接
题意:给你一棵树,让你求每个点之间的距离的最短之和
题解:看到最短之和,想到最小生成树,且每条边权不同,最小生成树唯一,就转换问题为,求一棵树上每个点到所有点的距离之和,这就是树形dp,先对一个点跑dfs,求出该点到所有点的距离之和,统计每个点的子树个数,并假设该点为根,再跑一次dfs统计\(dp\)
设\(dp_i\)为第i个节点到每个点的路径之和,假设\(j\)是他的子节点,容易知,有\(siz_j\)个节点到\(j\)的距离比到\(i\)少权值\(w\),有\(n-siz_j\)个节点到\(j\)的距离比到\(i\)多权值\(w\)
那就可以得到关系式:\(dp_j = dp_i - w*siz_j + w*(n-siz_j) = dp_i - w*(n-2*siz_j)\)
因为有1e6条边,用prim要用链式前向星存图即可
#include<bits/stdc++.h>
using namespace std;
#define ms(x,y) memset(x, y, sizeof(x))
#define lowbit(x) ((x)&(-x))
typedef long long LL;
typedef pair<int,int> pii;
const int maxn = 1e5+7;
const int maxm = 1e6+7;
int head1[maxm<<1], head2[maxn<<1], edgecnt1, edgecnt2, siz[maxn], n, m;
bool vis[maxn];
LL dp[maxn];
struct Node {
int u, v, w, nex;
} edges1[maxm<<1], edges2[maxn<<1];
struct edge {
int u, v, w;
edge(int _u, int _v, int _w):u(_u), v(_v), w(_w){}
bool operator < (const edge a) const {
return a.w < w;
}
};
void addedge1(int u, int v, int w) {
edges1[edgecnt1] = Node{u, v, w, head1[u]};
head1[u] = edgecnt1++;
edges1[edgecnt1] = Node{v, u, w, head1[v]};
head1[v] = edgecnt1++;
}
void addedge2(int u, int v, int w) {
edges2[edgecnt2] = Node{u, v, w, head2[u]};
head2[u] = edgecnt2++;
edges2[edgecnt2] = Node{v, u, w, head2[v]};
head2[v] = edgecnt2++;
}
void init() {
ms(head1, -1), ms(head2, -1), ms(vis, false);
edgecnt1 = edgecnt2 = 0;
dp[1] = 0;
}
LL prim() {
LL minsum = 0;
priority_queue<edge> q;
vis[1] = true;
for(int i = head1[1]; i != -1; i = edges1[i].nex) {
int v = edges1[i].v, w = edges1[i].w;
q.push(edge(1, v, w));
}
while(!q.empty()) {
auto now = q.top();
q.pop();
if(vis[now.v]) continue;
vis[now.v] = true;
minsum += now.w;
int u = now.v;
addedge2(now.u, u, now.w);
for(int i = head1[u]; i != -1; i = edges1[i].nex) {
int v = edges1[i].v, w = edges1[i].w;
if(vis[v]) continue;
q.push(edge{u, v, w});
}
}
return minsum;
}
void dfs1(int u, int fa, LL val, LL &ans) {
siz[u] = 1;
ans += val;
for(int i = head2[u]; i != -1; i = edges2[i].nex) {
int v = edges2[i].v;
if(v == fa) continue;
dfs1(v, u, val+edges2[i].w, ans);
siz[u] += siz[v];
}
}
void dfs2(int u, int fa, LL &ans) {
for(int i = head2[u]; i != -1; i = edges2[i].nex) {
int v = edges2[i].v, w = edges2[i].w;
if(v == fa) continue;
dp[v] = dp[u] + 1LL*w*(n-2*siz[v]);
ans += dp[v];
dfs2(v, u, ans);
}
}
void run_case() {
init();
cin >> n >> m;
for(int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
addedge1(u, v, w);
}
LL ans1 = prim();
dfs1(1, 0, 0, dp[1]);
LL ans2 = dp[1];
dfs2(1, 0, ans2);
cout << ans1 << " " << (double)ans2/n/(n-1) << "\n";
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cout.flags(ios::fixed);cout.precision(2);
int t; cin >> t;
while(t--)
run_case();
cout.flush();
return 0;
}
标签:最小生成树 ns2 Fix int cas set def false 链式
原文地址:https://www.cnblogs.com/GRedComeT/p/12379894.html