题目链接
题目链接:给你n个点,每个点都有权值,可以免费连接两个点,然后再选择n-2条边构成一个生成树,使得免费连接的两个点的权值和/n-2条边的长度和比值最大。输出这个最大值。
分析:很容易可以想到枚举哪两个点免费连接,然后用并查集维护,构建最小生成树,但这样做明显不现实,n=1000,时间复杂度不允许,所以否决这个方案。
反过来思考,如果不是在枚举点后求最小生成树,而是枚举最小生成树上的两点,因为树上路径是唯一的,所以求出这两点间最大的一条边,用总权值减去这一条边的长度,最后维护一个最小值就行了。
求路径的最小值这题的范围可以直接dfs暴力求,也可以树链剖分或者倍增。
这里用的倍增
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<vector>
#define MAIN main
#define PII pair<int,int>
#define x first
#define y second
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e3+10;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
struct edge
{
int v,next;
double w;
}e[(N*N)<<1];
int head[N],cnt;
void add(int u,int v,double w)
{
e[++cnt].v=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
struct node
{
int u,v;
double w;
bool operator < (const node &a)const {
return w<a.w;
}
}ee[N*N];
struct Point
{
double x,y;
int num;
Point (double x=0,double y=0,int num=0):x(x),y(y),num(num){
}
};
Point p[N];
int n,m;
double Distance(Point a,Point b)
{
double d1=a.x-b.x;
double d2=a.y-b.y;
return sqrt(d1*d1+d2*d2);
}
int f[N];
int Find(int x)
{
if(f[x]!=x) f[x]=Find(f[x]);
return f[x];
}
int fa[N],anc[N][30],dep[N];
double cost[N],maxcost[N][30];
void dfs(int u,int p)
{
fa[u]=p;
dep[u]=dep[p]+1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==p) continue;
cost[v]=e[i].w;
dfs(v,u);
}
}
void pre()
{
for(int i=1;i<=n;i++){
anc[i][0]=fa[i];maxcost[i][0]=cost[i];
for(int j=1;(1<<j)<n;j++) anc[i][j]=-1;
}
for(int j=1;(1<<j)<n;j++){
for(int i=1;i<=n;i++){
if(anc[i][j-1]!=-1){
int a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
maxcost[i][j]=max(maxcost[i][j-1],maxcost[a][j-1]);
}
}
}
}
double query(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
int log;
for(log=1;(1<<log)<=dep[u];log++);
log--;
double ans=-1e10;
for(int i=log;i>=0;i--){
if(dep[u]-(1<<i)>=dep[v]){
ans=max(ans,maxcost[u][i]);
u=anc[u][i];
}
}
if(u==v) return ans;
for(int i=log;i>=0;i--){
if(anc[u][i]!=anc[v][i]&&anc[u][i]!=-1){
ans=max(ans,maxcost[u][i]);u=anc[u][i];
ans=max(ans,maxcost[v][i]);v=anc[v][i];
}
}
ans=max(ans,cost[u]);
ans=max(ans,cost[v]);
return ans;
}
double mst;
void solve()
{
double ans=-1e10;
double sum;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
double max_edge=query(i,j);
mst-=max_edge;
sum=p[i].num+p[j].num;
ans=max(ans,sum/mst);
mst+=max_edge;
}
}
printf("%.2f\n",ans);
}
int MAIN()
{
int t;
scanf("%d",&t);
while(t--)
{
cnt=0;
memset(head,0,sizeof(head));
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf%d",&p[i].x,&p[i].y,&p[i].num);
}
m=0;
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
ee[++m].u=i;
ee[m].v=j;
ee[m].w=Distance(p[i],p[j]);
}
}
sort(ee+1,ee+m+1);
for(int i=1;i<=n;i++) f[i]=i;
int k=0;
mst=0;
for(int i=1;i<=m;i++)
{
int r1=Find(ee[i].u);
int r2=Find(ee[i].v);
if(r1==r2) continue;
k++;
f[r2]=r1;
mst+=ee[i].w;
add(ee[i].u,ee[i].v,ee[i].w);
add(ee[i].v,ee[i].u,ee[i].w);
//printf("%d %d %.2f\n",ee[i].u,ee[i].v,ee[i].w);
if(k==n-1) break;
}
dfs(1,0);
fa[1]=-1;
pre();
solve();
}
return 0;
}