洛谷· [AHOI2008]紧急集合 / 聚会

初见安~这里是传送门:洛谷P4281紧急集合/聚会

题目描述

欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。

参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在N个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。

小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?

输入格式:

第一行两个正整数N和M(N<=500000,M<=500000),之间用一个空格隔开。分别表示等待点的个数(等待点也从1到N进行编号)和获奖所需要完成集合的次数。 随后有N-1行,每行用两个正整数A和B,之间用一个空格隔开,表示编号为A和编号为B的等待点之间有一条路。 接着还有M行,每行用三个正整数表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。

输出格式:

一共有M行,每行两个数P,C,用一个空格隔开。其中第i行表示第i次集合点选择在编号为P的等待点,集合总共的花费是C个游戏币。

输入样例#1: 

6 4  
1 2  
2 3  
2 4 
4 5
5 6
4 5 6
6 3 1
2 4 4 
6 6 6

输出样例#1: 

5 2
2 5
4 1
6 0

说明

40%的数据中N<=2000,M<=2000
100%的数据中,N<=500000,M<=500000

题解

一开始还以为直接3个点求LCA(最近公共祖先)【传送门建设中】然后求路径就过去了。结果……连样例都过不了。

为什么不能直接3个点一起求LCA,因为我们看样例就可以发现一件事情:

点4、5、6集合时,最近的方案当然是都到点5去,但是直接求3个点的LCA的话答案一定是在点4集合。

那么我们怎么把5搞出来呢——很明显,5是5和6的LCA。4需要做的就是过来集合就行了。所以我们可以3次两两求LCA——求出来过后求剩下那个点到这个公共祖先的距离,作比较,得到最小的一个,就行了。

思路很简单,细节有点复杂,略显暴力。但是还是可以的。

当然为了力求LCA快一些,本人用了树链剖分来求LCA。会特别注释一下的。

上代码及详解——

#include<bits/stdc++.h>
#define maxn 500005
using namespace std;
int n, m;
struct edge
{
    int to, nxt;
    edge(){}
    edge(int tt, int nn)
    {
        to = tt, nxt = nn;
    }
}e[maxn << 1];

int head[maxn], k = 0;
void add(int u, int v)
{
    e[k] = edge(v, head[u]);
    head[u] = k++;
}

int dep[maxn], size[maxn], son[maxn], fa[maxn];
void dfs_getson(int u)//树剖初始化1
{
    size[u] = 1;
    int v;
    for(int i = head[u]; ~i; i = e[i].nxt)
    {
        v = e[i].to;
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs_getson(v);
        size[u] += size[v];
        if(size[v] > size[son[u]]) son[u] = v;
    }
}

int top[maxn];
void dfs_rewrite(int u, int tp)//树剖初始化2
{
    top[u] = tp;
    int v;
    if(son[u]) dfs_rewrite(son[u], tp);
    for(int i = head[u]; ~i; i = e[i].nxt)
    {
        v = e[i].to;
        if(v != fa[u] && v != son[u]) dfs_rewrite(v, v);
    }
}

struct node//为了方便处理答案开的结构体
{
    int x, ans;
    node(){}
    node(int xx, int aa)
    {
        x = xx, ans = aa;
    }
};

node ask1(int u, int v)//树剖求解LCA和路径(不用在意那个1,之前想复杂了就有个ask2
{
    int ans = 0;
    while(top[u] != top[v])
    {
        if(dep[top[u]] > dep[top[v]]) swap(u, v);
        ans += dep[v] - dep[fa[top[v]]];
        v = fa[top[v]];
    }
    if(dep[u] > dep[v]) swap(u, v);
    ans += dep[v] - dep[u];
    return node(u, ans);
}

int main()
{
    memset(head, -1, sizeof head);
    scanf("%d%d", &n, &m);
    int u, v, w;
    for(int i = 1; i < n; i++)
    {
        scanf("%d%d", &u, &v);
        add(u, v);
        add(v, u);
    }
    
    dfs_getson(1);
    dfs_rewrite(1, 1);
    
    while(m--)
    {
        scanf("%d%d%d", &u, &v, &w);
        node tmp1, ans = node(0, 0x3f3f3f3f), tmp2;
        //三次分别LCA
        tmp1 = ask1(u, v);
        tmp2 = ask1(tmp1.x, w);
        if(tmp1.ans + tmp2.ans < ans.ans) ans = node(tmp1.x, tmp1.ans + tmp2.ans);
        
        tmp1 = ask1(v, w);
        tmp2 = ask1(tmp1.x, u);
        if(tmp1.ans + tmp2.ans < ans.ans) ans = node(tmp1.x, tmp1.ans + tmp2.ans);
        
        tmp1 = ask1(u, w);
        tmp2 = ask1(tmp1.x, v);
        if(tmp1.ans + tmp2.ans < ans.ans) ans = node(tmp1.x, tmp1.ans + tmp2.ans);
        printf("%d %d\n", ans.x, ans.ans);
    }
    return 0;
}
/*
10 1
1 2
1 3
1 10
2 4
2 5
2 6
3 7
7 8
7 9
4 6 10
这是自己当时查错时的数据QwQ然后就想到了暴力LCA 3次
*/

迎评:)
——End——

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值