22中超联赛day8 1007(hdu7226) Darnassus 并查集(按秩合并+路径压缩)+ 链式前向星桶排 + Kruskal求最小生成树

Darnassus

题目描述

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

Archimonde has hurt it once, Sylvanas burnt it again.

Now the World Tree is slowly recovering.

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

Each part of the World Tree has an attribute p i p_i pi, and all p i p_i pi ( 1 ≤ i ≤ n ) (1≤i≤n) (1in) forms a permutation of 1 , 2 , 3... n 1,2,3...n 1,2,3...n.

For all 1 ≤ i < j ≤ n 1≤i<j≤n 1i<jn, if the World Tree wants to grow an edge connecting part i i i and part j j j directly, it needs to spend ∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|pi-pj| ijpipj energy. ∣ x ∣ |x| x means the absolute value of x x x.

The World Tree is very smart, so it will grow some edges such that all its n 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.

输入描述

The input consists of multiple test cases.

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

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

The second line contains n n n integers p i ( 1 ≤ p i ≤ n ) p_i (1≤p_i≤n) pi(1pin), it’s guaranteed that all p i p_i pi forms a permutation.

输出描述

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

输入样例:

2
5
4 3 5 1 2
10
4 7 3 8 6 1 9 10 5 2

输出样例:

7
24

题意

给定 n n n 个点编号为 1 − n 1-n 1n 以及一个 1 − n 1-n 1n 的排列 任意两个点 i , j i ,j i,j 之间存在一条边权为 ∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|pi-pj| ijpipj 的边 求将n个点联通的最小权值和

思路

∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|pi-pj| ijpipj 根据该式可知取 ∣ i − j ∣ = = 1 |i-j|==1 ij==1 的相邻两数连边权值 ≤ n − 1 ≤n-1 n1 故其中的一种构建生成树的方案是选择 n − 1 n-1 n1 条两两相邻的边。故最小生成树中必定不存在权值 > n − 1 >n-1 >n1 的边
证:若存在任意一条权值 > n − 1 >n-1 >n1 连通任意两个连通块
i ) i) i) 两个联通块相邻 那么可以直接选择连通两个连通块相邻的两个点权值 ≤ n − 1 ≤n-1 n1
i i ) ii) ii) 两个联通块不相邻 证明两连通块间存在其它的一个或多个连通块 将相邻的连通块使用权值 ≤ n − 1 ≤n-1 n1 的相邻边连同后又转化为第一种两个连通块相邻的情况
故选定的边满足 ∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|pi-pj| ijpipj ≤ n − 1 ≤n-1 n1
∣ i − j ∣ |i-j| ij ∣ p i − p j ∣ |pi-pj| pipj 两项必有一项 ≤ ( n − 1 ) ≤\sqrt{(n-1)} (n1)
可以在 O ( n n ) O(n\sqrt{n}) O(nn ) 的时间内枚举出满足条件的边
i ) i) i) ∣ i − j ∣ |i-j| ij ≤ ( n − 1 ) ≤\sqrt{(n-1)} (n1) 即两点的下标 ≤ ( n − 1 ) ≤\sqrt{(n-1)} (n1)
i i ) ii) ii) ∣ p i − p j ∣ |pi-pj| pipj ≤ ( n − 1 ) ≤\sqrt{(n-1)} (n1) 即两点的点权 ≤ ( n − 1 ) ≤\sqrt{(n-1)} (n1)
开两个数组分别记录枚举即可
最多可得到 2 ∗ n n 2*n\sqrt{n} 2nn 条边 直接 s o r t sort sort t t t
因选取的边权值都 ≤ n − 1 ≤n-1 n1 值域较小 所以可以采用链式前向星桶排
这样在 K r u s k a l Kruskal Kruskal 求最小生成树时只需要从小到大枚举值域即可

Code

#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
#define endl '\n'
const int mod=998244353;
const double PI= acos(-1);
const double eps=1e-6;
const int N=2e5+7;

long long x,ans;
int n,cnt,nd;
long long p[50003],id[500003];
int b[50003],dep[500003],h[50003];
int fd(int x)
{
    if(b[x]==x)return x;
    return b[x]=fd(b[x]);//路径压缩
}
struct node{
    int x,y,nx;
}e[24000003];

void merge(int x,int y)
{
    x=fd(x);
    y=fd(y);
    if(dep[x]<dep[y])
    {
        b[x]=y;
    }
    else if(dep[x]==dep[y])
    {
        b[x]=y;
        ++dep[y];
    }
    else
    {
        b[y]=x;
    }//按秩合并
}

inline void sol()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        b[i]=i;
        dep[i]=1;
        h[i]=-1;
        scanf("%d",&p[i]);
        id[p[i]]=i;
    }
    ans=0;
    cnt=0;
    nd=n-1;
    for(int i=1;i<n;++i)
    {
        for(int j=1;j*j<=n&&i+j<=n;++j)//向后枚举 sqrt(n) 个点/值
        {
            x=abs(i-(i+j))*abs(p[i]-p[i+j]);
            if(x<n)//将边权小于等于 n-1 的放入对应权值的桶
            {
                e[++cnt].x=i;
                e[cnt].y=i+j;
                e[cnt].nx=h[x];
                h[x]=cnt;
            }
            x=abs(id[i]-id[i+j])*abs(i-(i+j));
            if(x<n)
            {
                e[++cnt].x=id[i];
                e[cnt].y=id[i+j];
                e[cnt].nx=h[x];
                h[x]=cnt;
            }
        }
    }
    for(int i=1;i<n;++i)//从小到大枚举值域
    {
        for(int j=h[i];~j;j=e[j].nx)
        {
            if(fd(e[j].x)!=fd(e[j].y))
            {
                merge(e[j].x,e[j].y);
                ans+=i;
                --nd;
            }
            if(nd==0)break;
        }
    }
    printf("%d\n",ans);
}
int main()
{
    //freopen("C:\\Users\\23122\\Desktop\\1.in","r",stdin);
    //freopen("C:\\Users\\23122\\Desktop\\out.txt","w",stdout);
    __T
    sol();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值