由于我好久都没有独立思考了(抄了好久题解),思维什么的早没有了。
看完题只能想到一种暴力:遍历+LCA
题目给出的是一个最小生成树,我们可以从边入手,把每条边所连的左右两个点分别看做一个集合,
左边集合和右边集合所要连的边数是(cnt[i]*cnt[j]-1),-1是因为要抛去本身这条边
我们要连的边权是多大?比当前的边大,
否则就不满足有且仅有一棵最小生成树T,所以边权就为(当前边的边权+1)
我们可以贪心的去解决,把边从小到大排序,并查集一下就行了,当然还得加上原始边的边权。
另外...ans的开long long
#include<cstdio> #include<algorithm> #define ll long long using namespace std; inline int read() { int sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } const int N = 1e5 + 5; int n,fa[N],cnt[N]; ll ans; struct edge { int frm,to,wei; }e[N]; bool cmp(edge a,edge b) { return a.wei < b.wei; } int findfa(int x) { return fa[x] == x ? x : findfa(fa[x]); } void kruskal() { for(int i = 1;i <= n;i++) { fa[i] = i; cnt[i] = 1; } sort(e+1,e+n,cmp); for(int i = 1;i <= n - 1;i++) { int u = findfa(e[i].frm); int v = findfa(e[i].to); if(u == v) continue; fa[v] = u; ans += (ll)(cnt[u] * cnt[v] - 1) * (e[i].wei + 1); cnt[u] += cnt[v]; } } int main() { n = read(); for(int i = 1;i <= n - 1;i++) { e[i].frm = read(); e[i].to = read(); e[i].wei = read(); ans += (ll)e[i].wei; } kruskal(); printf("%lld",ans); return 0; }