题目链接:http://codeforces.com/problemset/problem/1245/D

思路

把点权转化为边权,建一个“超级根”,设其编号为0,把它与原有的n个点相连,形成n条边,把这n条边的边权赋值为n个点的点权。
剩下的就是把所有边权依次存入数组。

然后就是把所有边权按从小到大排序,跑一遍最小生成树即可。

(做的时候想到了可能和最小生成树有关,但是没有想到建一个新根再把点权转化为边权这种神奇的操作,这思路真是挺巧妙的!)

AC代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+10;
ll n,cnt,num,tot,sum,ans,pre[N],power[N];
struct edge
{
    ll a,b,w;
}e[N*N],line[N*N];
struct node
{
    ll x,y,w,val;
}p[N];
ll find(ll x)
{
    if(x!=pre[x])pre[x]=find(pre[x]);
    return pre[x];
}
void join(ll a,ll b)
{pre[find(a)]=find(b);}
bool cmp(edge s1,edge s2)
{return s1.w<s2.w;}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>p[i].x>>p[i].y;
    for(int i=1;i<=n;i++)
    {
        cin>>p[i].val;//点权
        e[++cnt].a=0;//起点为新根0
        e[cnt].b=i;//终点为i
        e[cnt].w=p[i].val;//点权转化为边权
    }
    for(int i=1;i<=n;i++)
        cin>>p[i].w;//暂时边权
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            e[++cnt].a=i;
            e[cnt].b=j;
            e[cnt].w=(abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y))*(p[i].w+p[j].w);//计算边权,存入e数组
        }
    for(int i=0;i<=n;i++)//从0开始初始化
        pre[i]=i;
    sort(e+1,e+cnt+1,cmp);
    for(int i=1;i<=cnt;i++)
    {
        if(find(e[i].a)!=find(e[i].b))
        {
            join(e[i].a,e[i].b);
            sum++;
            ans+=e[i].w;
            if(e[i].a==0)power[++num]=e[i].b;//发电站
            else line[++tot]=e[i];//要连线的边
            if(sum==n)break;//总共n+1个点(因为加了一个根),如果已经连了n条边,就结束
        }
    }
    printf("%lld\n",ans);//最小权值和
    printf("%lld\n",num);
    for(int i=1;i<=num;i++)
        i==num?printf("%lld\n",power[i]):printf("%lld ",power[i]);
    printf("%lld\n",tot);
    for(int i=1;i<=tot;i++)
        printf("%lld %lld\n",line[i].a,line[i].b);
    return 0;
}