[kruskal]Darnassus 2022杭电多校第8场 1007

48 篇文章 0 订阅

Problem Description

\space \space  Even the World Tree must bow to the cycle of life. Everything born will die.

\space \space  Archimonde has hurt it once, Sylvanas burnt it again.

\space \space  Now the World Tree is slowly recovering.

The World Tree is burnt apart into nn parts. Now it tries to rebuild itself.

Each part of the World Tree has an attribute p_ipi​, and all p_i\ (1\leq i\leq n)pi​ (1≤i≤n) forms a permutation of 1,2,3...n1,2,3...n.

For all 1 \leq i < j \leq n1≤i<j≤n, if the World Tree wants to grow an edge connecting part ii and part jj directly, it needs to spend |i-j| * |p_i-p_j|∣i−j∣∗∣pi​−pj​∣ energy. |x|∣x∣ means the absolute value of xx.

The World Tree is very smart, so it will grow some edges such that all its nn parts become connected (in other words, you can go from any part to any other part using only the edges that have been grown), spending the minimum energy.

Please calculate the minimum energy the World Tree needs to spend.

Input

The input consists of multiple test cases.

The first line contains an integer T\ (1\leq T \leq 5)T (1≤T≤5) denoting the number of test cases.

For each test case, the first line contains a single integer n(1 \leq n \leq 50000)n(1≤n≤50000).

The second line contains nn integers p_i\ (1 \leq p_i \leq n)pi​ (1≤pi​≤n), it's guaranteed that all p_ipi​ forms a permutation.

Output

For each test case, output one line containing one integer indicating the answer.

Sample Input

2

5

4 3 5 1 2

10

4 7 3 8 6 1 9 10 5 2

Sample Output

7

24

题意: 有n个点,给出一个长度为n的全排列a,第i个点的点权是ai,对于这n个点之间可以任意连边,边权为|j-i|*|a[j]-a[i]|,问形成的连通图的最小生成树是多少。

分析: 如果将所有的i和i+1连边,那么最后形成的树每条边边权都小于等于n-1,根据kruskal算法的过程,可以知道最终最小生成树中边权一定都小于等于n-1,所以只需要找到|j-i|*|a[j]-a[i]| <= n-1的边加进去跑kruskal就能得到答案,而这些边满足|j-i|和|a[j]-a[i]|中至少有一个 <= sqrt(n-1),所以只需要找|j-i| <= sqrt(n-1)的边和|a[j]-a[i]| <= sqrt(n-1)的边加入边集就行了,这个过程是O(n^1.5)的,之后注意不能直接对边集sort,这样会超时,不过可以选择O(n)的桶排序,最后就是跑一个常规的kruskal算法得到答案。

要注意的是桶排序的时候用vector数组可能会比用链表(也就是链式前向星)慢一点,虽然用快读或火车头可以水过去,但最好还是用链表实现。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;

int fa[50005], pos[50005], a[50005];
vector<int> box[50005];

struct edge{
	int u, v, w;
}e[50005*600];

int find(int x){
	if(x == fa[x]) return x;
	return fa[x] = find(fa[x]);
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0); 
	int T;
	cin >> T;
	while(T--){
		int n;
		cin >> n; 
		for(int i = 1; i <= n; i++){
			fa[i] = i;		
			cin >> a[i];
			pos[a[i]] = i;
			box[i].clear();
		}
		int top = sqrt(n-1);
		int m = 0;
		for(int i = 1; i <= n; i++)
			for(int j = i+1; j-i <= min(top, n-i); j++){
				long long w1 = 1ll*(j-i)*abs(a[j]-a[i]);
				long long w2 = 1ll*(j-i)*abs(pos[j]-pos[i]);
				if(w1 <= n-1){//j-i <= top
					++m;
					e[m].u = i, e[m].v = j, e[m].w = w1;
					box[w1].push_back(m);
				}
				if(w2 <= n-1){//a[j]-a[i] <= top
					++m;
					e[m].u = pos[i], e[m].v = pos[j], e[m].w = w2;
					box[w2].push_back(m);
				}
			} 
		long long ans = 0;
		int cnt = 0;
		for(int i = 1; i <= n-1; i++){
			for(int j = 0; j < box[i].size(); j++){
				int id = box[i][j];
				int u = e[id].u, v = e[id].v, w = e[id].w;
				if(find(u) != find(v)){
					fa[find(u)] = find(v);
					ans += w;
					cnt++;
					if(cnt == n-1) break;
				}
			}
			if(cnt == n-1) break;
		}
		cout << ans << endl;
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值