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) (1≤i≤n) 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 1≤i<j≤n, 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| ∣i−j∣∗∣pi−pj∣ 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(1≤T≤5) 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(1≤n≤50000).
The second line contains n n n integers p i ( 1 ≤ p i ≤ n ) p_i (1≤p_i≤n) pi(1≤pi≤n), 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.
输入样例:
25
4 3 5 1 2
10
4 7 3 8 6 1 9 10 5 2
输出样例:
724
题意
给定 n n n 个点编号为 1 − n 1-n 1−n 以及一个 1 − n 1-n 1−n 的排列 任意两个点 i , j i ,j i,j 之间存在一条边权为 ∣ i − j ∣ ∗ ∣ p i − p j ∣ |i-j|*|pi-pj| ∣i−j∣∗∣pi−pj∣ 的边 求将n个点联通的最小权值和
思路
∣
i
−
j
∣
∗
∣
p
i
−
p
j
∣
|i-j|*|pi-pj|
∣i−j∣∗∣pi−pj∣ 根据该式可知取
∣
i
−
j
∣
=
=
1
|i-j|==1
∣i−j∣==1 的相邻两数连边权值
≤
n
−
1
≤n-1
≤n−1 故其中的一种构建生成树的方案是选择
n
−
1
n-1
n−1 条两两相邻的边。故最小生成树中必定不存在权值
>
n
−
1
>n-1
>n−1 的边
证:若存在任意一条权值
>
n
−
1
>n-1
>n−1 连通任意两个连通块
i
)
i)
i) 两个联通块相邻 那么可以直接选择连通两个连通块相邻的两个点权值
≤
n
−
1
≤n-1
≤n−1
i
i
)
ii)
ii) 两个联通块不相邻 证明两连通块间存在其它的一个或多个连通块 将相邻的连通块使用权值
≤
n
−
1
≤n-1
≤n−1 的相邻边连同后又转化为第一种两个连通块相邻的情况
故选定的边满足
∣
i
−
j
∣
∗
∣
p
i
−
p
j
∣
|i-j|*|pi-pj|
∣i−j∣∗∣pi−pj∣
≤
n
−
1
≤n-1
≤n−1
∣
i
−
j
∣
|i-j|
∣i−j∣ 或
∣
p
i
−
p
j
∣
|pi-pj|
∣pi−pj∣ 两项必有一项
≤
(
n
−
1
)
≤\sqrt{(n-1)}
≤(n−1)
可以在
O
(
n
n
)
O(n\sqrt{n})
O(nn) 的时间内枚举出满足条件的边
i
)
i)
i)
∣
i
−
j
∣
|i-j|
∣i−j∣
≤
(
n
−
1
)
≤\sqrt{(n-1)}
≤(n−1) 即两点的下标
≤
(
n
−
1
)
≤\sqrt{(n-1)}
≤(n−1)
i
i
)
ii)
ii)
∣
p
i
−
p
j
∣
|pi-pj|
∣pi−pj∣
≤
(
n
−
1
)
≤\sqrt{(n-1)}
≤(n−1) 即两点的点权
≤
(
n
−
1
)
≤\sqrt{(n-1)}
≤(n−1)
开两个数组分别记录枚举即可
最多可得到
2
∗
n
n
2*n\sqrt{n}
2∗nn 条边 直接
s
o
r
t
sort
sort 会
t
t
t
因选取的边权值都
≤
n
−
1
≤n-1
≤n−1 值域较小 所以可以采用链式前向星桶排
这样在
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;
}