题目:

样例输入:
- 2
- 5
- 4 3 5 1 2
- 10
- 4 7 3 8 6 1 9 10 5 2
样例输出:
- 7
- 24
题意:给出一个排列p,把每个位置视为点,建一个无向图,i,j之间的边权为|i-j|*|pi-pj|。求这个图的最小生成树。
先来给出最小生成树的一些性质:
(1)一个图的最小生成树不一定唯一,但是其对应第k大边权一定是唯一的
(2)最小生成树的最大边权一定是所有生成树的最大边权的最小值
分析:首先我们尝试着把相邻的点之间连一条边,这样每两个相邻点之间的边权都是|pi-pj|
下面是代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- using namespace std;
- const int N=5e4+10;
- int fu[N],a[N],mp[N];
- int h[N],ne[N*600],idx,u[N*600],v[N*600];
- struct node{
- int u,v,w;
- }p[N*600];
- int find(int x)
- {
- if(x!=fu[x]) fu[x]=find(fu[x]);
- return fu[x];
- }
- void add(int x,int y,int z)
- {
- u[idx]=x;
- v[idx]=y;
- ne[idx]=h[z];
- h[z]=idx++;
- }
- int main()
- {
- int T;
- cin>>T;
- while(T--)
- {
- int n;
- scanf("%d",&n);
- idx=0;
- for(int i=1;i<=n;i++)
- {
- fu[i]=i;h[i]=-1;
- scanf("%d",&a[i]);
- mp[a[i]]=i;
- }
- int cnt=0;
- for(int i=1;i<=n;i++)
- {
- int t=(int)sqrt(n);
- int e=min(i+t,n);
- for(int j=i+1;j<=e;j++)
- {
- int tt=1ll*(j-i)*abs(a[j]-a[i]);
- if(tt
- add(i,j,tt);
- tt=1ll*abs(mp[j]-mp[i])*(j-i);
- if(tt
- add(mp[i],mp[j],tt);
- }
- }
- long long ans=0;
- int count=0;
- for(int i=1;i
- {
- for(int j=h[i];j!=-1;j=ne[j])
- {
- int fx=find(u[j]),fy=find(v[j]);
- if(fx==fy) continue;
- fu[fx]=fy;
- count++;ans+=i;
- if(count==n-1) break;
- }
- if(count==n-1) break;
- }
- printf("%lld\n",ans);
- }
- return 0;
- }
-
相关阅读:
【攻防世界】unseping (反序列化与Linux bash shell)
L76.linux命令每日一练 -- 第11章 Linux系统管理命令 -- free和iftop
数据可视化之快递行业发展,近几年我国快递行业业务量呈梯度级增长
Docker
【Linux】进程控制
达索系统SOLIDWORKS Electrical机电一体化协同设计
[算法周训 3] 字符串训练2
动态整数多重集的高效实现与操作
关于MySql中explain结果filtered的理解
接口的安全设计的几大要素:ticket,签名,时间戳
-
原文地址:https://blog.csdn.net/AC__dream/article/details/126308484