Typesetting math: 0%

[CSP-S模拟测试]:影子(并查集+LCA)


题目描述

一个人有很多的影子,新的旧的,他们不断消失重来。学者的影子在他苍白色的精神图景里成为了n个黑色的点,他们伸长的触手交叉形成了一颗黑色的树。假使每个影子点拥有一个权值di,黑色的树边也有一个权值wi,对于一条黑色树的路径,令路径上所有影子点权值di的最小值为mind,路径上所有树边权值wi的总和为sumw,则该条路径的总权值为mind×sumw。路径的起点和终点可以是黑色树中的任意影子点,且路径中不能出现重复的影子点。
现在学者需要知道这棵黑色树里所有路径总权值中的最大值为多少


输入格式

第一行输入一个整数T,表示数据组数
每组数据第一行一个正整数n,表示影子点总数
之后n个整数,表示每个影子点的权值
之后n1行,每行三个整数u,v,w表示一条连接u,v且权值为w的树边。数据保证不会出现重边,且一定构成树结构


输出格式

一共T行,表示当前这组数据的答案


样例

样例输入:

1
3
1 2 3
1 2 1
1 3 2

样例输出:

3


数据范围与提示

样例解释:

总权值最大的路径是213mindmin(1,2,3)=1sumwsum(1,2)=3,因此答案为1×3=3

数据范围:

对于100%的数据1T10,1n105,1di109,1u,vn,1wi109
其中35%的数据1n100
其中最多有50%的数据保证104n105


题解

再一次没有打正解,显然正解是点分治。

然而我用的是比较玄学的并查集。

首先,将所有的点权从大到小排序,于将当前点和与其相连的所有点依次合并到一个集合中。

并查集维护当前集合中的最长路径长度和对应的两个端点。

保证当前这个点是并查集中最大的点,这样它肯定对mind没有贡献。

那么,合并两个集合后集合的最长路分为两种情况:

  α.其中一个集合的最长路。

  β.两个集合的最长路的端点相互连接。

这里就需要用到LCA了。

每次合并并查集之后用当前点的权值乘以最长路的总长度来更新最优结果即可。即使这个点不在当前合并后的集合的最长路上也是没有问题的,因为如果这样的话,必然已经在之前得到了对应的结果,这次合并不会对最终结果产生影响。

时间复杂度:Θ(nlogn)

期望得分:100分。

实际得分:100分。


代码时刻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to,w;}e[200001];
int head[100001],cnt;
int n;
int f[100001],lft[100001],rht[100001],rk[100001];
int fa[100001][21],depth[100001];
long long dis[100001],len[100001];
pair<int,int> d[100001];
long long ans;
void pre_work()
{
    memset(len,0,sizeof(len));
    memset(head,0,sizeof(head));
    memset(depth,0,sizeof(depth));
    ans=cnt=0;
    for(int i=1;i<=n;i++)f[i]=lft[i]=rht[i]=i;
}
void add(int x,int y,int w)
{
    e[++cnt].nxt=head[x];
    e[cnt].to=y;
    e[cnt].w=w;
    head[x]=cnt;
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void dfs(int x)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        if(depth[e[i].to])continue;
        depth[e[i].to]=depth[x]+1;
        fa[e[i].to][0]=x;
        for(int j=1;j<=20;j++)
            fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
        dis[e[i].to]=dis[x]+e[i].w;
        dfs(e[i].to);
    }
}
int LCA(int x,int y)
{
    int minn=1<<30;
    if(depth[x]>depth[y])swap(x,y);
    for(int i=20;~i;i--)
        if(depth[fa[y][i]]>=depth[x])
            y=fa[y][i];
    if(x==y)return x;
    for(int i=20;~i;i--)
        if(fa[x][i]!=fa[y][i])
        {
            x=fa[x][i];
            y=fa[y][i];
        }
    return fa[x][0];
}
long long get_dis(int x,int y){return dis[x]+dis[y]-(dis[LCA(x,y)]<<1);}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        pre_work();
        for(int i=1;i<=n;i++)
        {
            int x;scanf("%d",&x);
            d[i]=make_pair(x,i);
        }
        sort(d+1,d+n+1,greater<pair<int,int> >());
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        depth[1]=1;
        dfs(1);
        for(int i=1;i<=n;i++)rk[d[i].second]=i;
        for(int i=1;i<=n;i++)
        {
            for(int j=head[d[i].second];j;j=e[j].nxt)
            {
                int to=find(e[j].to);
                if(rk[d[i].second]<rk[to])continue;
                len[0]=len[d[i].second],lft[0]=lft[d[i].second],rht[0]=rht[d[i].second];
                if(len[to]>len[0]){len[0]=len[to];lft[0]=lft[to];rht[0]=rht[to];}
                if((dis[0]=get_dis(lft[d[i].second],lft[to]))>len[0]){len[0]=dis[0];lft[0]=lft[d[i].second];rht[0]=lft[to];}
                if((dis[0]=get_dis(rht[d[i].second],rht[to]))>len[0]){len[0]=dis[0];lft[0]=rht[d[i].second];rht[0]=rht[to];}
                if((dis[0]=get_dis(lft[d[i].second],rht[to]))>len[0]){len[0]=dis[0];lft[0]=lft[d[i].second];rht[0]=rht[to];}
                if((dis[0]=get_dis(rht[d[i].second],lft[to]))>len[0]){len[0]=dis[0];lft[0]=rht[d[i].second];rht[0]=lft[to];}
                f[to]=d[i].second;lft[d[i].second]=lft[0];rht[d[i].second]=rht[0];len[d[i].second]=len[0];
            }
            ans=max(ans,len[d[i].second]*d[i].first);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

rp++

posted @   HEOI-动动  阅读(264)  评论(0编辑  收藏  举报
编辑推荐:
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
阅读排行:
· 手把手教你更优雅的享受 DeepSeek
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库
· 乌龟冬眠箱湿度监控系统和AI辅助建议功能的实现
点击右上角即可分享
微信分享提示