漂亮的公园 O(|S|)的复杂度求点集S的直径

题目

漂亮的公园 (https://ac.nowcoder.com/acm/problem/18391)

题目描述

小N所在城市有n个漂亮的公园。有恰好n-1条双向道路连接这n个公园,保证公园间相互可以通过道路到达。每个公园i都有一个专属的属性c[i],表示这个公园的特色。
现在小N有q个疑问。每次他会有两个特定的特色x和y(这两个数可能相同)。他想知道,假如他随便选取一个特色为x的公园出发,必须走到一个特色为y的公园结束,在最优情况下,他最远能走过多少条道路。换句话说,枚举所有的满足c[p] = x的公园p,所有满足c[q] = y的公园q,求出p,q距离的最大值。

输入描述:

第一行两个整数n和q。
第二行n个整数,第i个整数表示第i个公园对应的特色c[i]。
接下来n-1行,每行两个整数u和v,表示有一条连接公园u和公园v的道路。
接下来q行,每行两个整数x和y,代表小N的一个疑问。
数据保证:n ≤ 10^5, q ≤ 10^5, 1 ≤ u, v ≤ n, 0 ≤ c[i], x, y ≤ 10^9

输出描述:

输出共q行,每行对于小N的一个疑问的答案。注意,假如一个疑问中,不存在一个公园特色为x,或不存在一个公园特色为y,那么输出答案0。代表小N一条边都走不了。

输入

10 4
9 8 9 8 9 8 7 7 8 7
4 5
5 10
10 3
3 9
9 1
1 8
5 7
10 6
8 2
5 8
8 8
7 9
8 9

输出

0
7
5
6

思路

最长距离我们自然联想到树的直径。如果我们把每个属性集合的直径都求出来。那么求p,q的直径。
假设p的直径(x1, y1),q的直径(x2, y2)。那么p集到q集的最远距离可能的组合为(x1, x2)
(x1, y2), (y1, x2), (y1, y2),我们取一个最大值就可以了。
现在的问题是我们怎么求每个属性的直径。我们把每个点的加入vector。根据直径的性质:距离点集任意点的最远点一定是直径的一个端点。
那么复杂度为2|s|就可以求出s集合的直径。求距离可以倍增+LCA。那么就可以n*logn求出所有的属性集合的直径。
回答每个询问O(1)就可以了

#include<bits/stdc++.h>
using namespace std;

struct edge{
    int to, nxt;
}e[100005<<1];

int head[100005], lg[200005], cut=0;

struct Tree_lca{

    int f[100005][22], deep[100005], in[100005], out[100005], T=0;
    void init(){
        memset(head, -1, sizeof(head));
        cut=T=0;
    }
    void addedge(int x, int y){
        e[++cut]={y, head[x]};
        head[x]=cut;
    }
    void dfs(int u, int fa){
        f[u][0]=fa; in[u]=++T; deep[u]=deep[fa]+1;
        for(int i=1; i<=lg[deep[u]]; i++){
            f[u][i]=f[f[u][i-1]][i-1];
        }
        for(int i=head[u]; i!=-1; i=e[i].nxt){
            int to=e[i].to;
            if(to!=fa){
                dfs(to, u);
            }
        }
        out[u]=++T;
    }
    int LCA(int x, int y){
        if(deep[x]<deep[y]) swap(x, y);
        int d=deep[x]-deep[y];
        for(int i=0; d; i++){
            if(d&(1<<i)){
                x=f[x][i];
                d-=(1<<i);
            }
        }
        if(x==y) return x;
        for(int i=lg[deep[y]]; i>=0; i--){
            if(f[x][i]!=f[y][i]){
                x=f[x][i], y=f[y][i];
            }
        }
        return f[x][0];
    }
    int dis(int x, int y){
        return deep[x]+deep[y]-2*deep[LCA(x, y)];
    }
    int getans(int x1, int y1, int x2, int y2){
        int pos[4]={x1, y1, x2, y2};
        int mx=0;
        for(int i=0; i<2; i++){
            for(int j=2; j<4; j++){
                mx=max(mx, dis(pos[i], pos[j]));
            }
        }
        return mx;
    }

}Tree;

int c[100005], d[100005][2];
vector<int> g[100005];
struct LSH{//离散化
    int b[100005];
    unordered_map<int, int> mp;//范围
    int lsh(int a[], int n){//得到离散化后不同元素的个数
        mp.clear();
        for(int i=1; i<=n; i++) b[i]=a[i];
        sort(b+1, b+n+1);
        int cnt=unique(b+1, b+n+1)-b-1;
        for(int i=1; i<=n; i++){
            int x=a[i];
            a[i]=lower_bound(b+1, b+cnt+1, a[i])-b;
            mp[x]=a[i];
        }
        return cnt;
    }
    int id(int x){//得到原树
        return mp[x];
    }
}Lsh;

int main() {

    lg[1]=1;
    for(int i=2; i<=100005; i++){
        lg[i]=lg[i>>1]+1;
    }
    int n, q; scanf("%d%d", &n, &q);
    for(int i=1; i<=n; i++){
        scanf("%d", &c[i]);
    }
    int m=Lsh.lsh(c, n);
    for(int i=1; i<=n; i++){
        g[c[i]].push_back(i);
    }

    Tree.init();
    for(int i=1;i<=n-1; i++){
        int x, y; scanf("%d%d", &x, &y);
        Tree.addedge(x, y); Tree.addedge(y, x);
    }
    Tree.dfs(1, 0);
    for(int i=1; i<=m; i++){
        d[i][0]=d[i][1]=g[i][0];
        int d1=Tree.dis(d[i][0], d[i][1]);
        for(int j=1; j<g[i].size(); j++){
            int x=g[i][j];
            int d2=Tree.dis(d[i][0], x), d3=Tree.dis(d[i][1], x);

            if(d2<d3) swap(d2,d3),swap(d[i][0],d[i][1]);
            if(d2>d1) d[i][1]=g[i][j],d1=Tree.dis(d[i][0],d[i][1]);
        }
    }
    for(int i=1; i<=q; i++){
        int x, y; scanf("%d%d", &x, &y);
        x=Lsh.mp[x], y=Lsh.mp[y];
        //printf("%d %d %d %d\n", d[x][0], d[x][1], d[y][0], d[y][1]);
        if(x==0||y==0){
            printf("0\n");
        }
        else{
            printf("%d\n", Tree.getans(d[x][0], d[x][1], d[y][0], d[y][1]));
        }
    }

    return 0;
}

posted @   liweihang  阅读(167)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『漂亮的公园 O(|S|)的复杂度求点集S的直径』
点击右上角即可分享
微信分享提示