第三次周赛D题

题意:

多次询问树上两点最短 距离。

TLE做法:对每次询问跑一遍最短路,用堆优化迪杰斯特拉的话复杂度大概为\(O(q*(n+m)logm)\)。
\(100\)分做法:
用一个\(dis\)数组记录根节点\(root\)到每一个节点的距离,那么树上两点\(u,v\)的距离就是\(root\)到\(u\)的距离加上\(root\)到\(v\)的距离减去两倍的\(root\)到\(lca(u,v)\)的距离(可以画下图感性理解...),即
\(ans=dis[u]+dis[v]-2*dis[lca(u,v)]\)
我用的是树上倍增求\(lca\)。

最近公共祖先(\(lca\))是指在一个树或者有向无环图中同时拥有\(v\)和\(w\)作为后代的最深的节点,最近公共祖先是两个节点所有公共祖先中离根节点最远的。

其实就是\(lca\)模板题,看几篇博客就会了。
具体求法可以参考一下大佬的博客浅谈最近公共祖先
最近公共祖先

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

#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define endl '\n'

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> pii;

const int maxn  = 75000 + 5;
const int maxm  = 100 + 5;
const int inf   = 0x3f3f3f3f;
const LL  mod   = 1e9 + 7;//19260817
const double pi = acos(-1.0);

int n, q, cnt, x, y, deep[maxn], pre[maxn][20], head[maxn], dis[maxn];

struct node {
    int v, w, next;
} edge[maxn << 1];

void addedge(int u, int v, int w) {
    edge[++cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

void dfs(int x, int f) {
    pre[x][0] = f;
    for(int i = head[x]; i; i = edge[i].next) {
        if(edge[i].v != f) {
            deep[edge[i].v] = deep[x] + 1;
            dis[edge[i].v] = dis[x] + edge[i].w;
            dfs(edge[i].v, x);
        }
    }
}

void solve() {
    for(int j = 1; j <= 19; j++)
        for(int i = 1; i <= n; i++)
            pre[i][j] = pre[pre[i][j - 1]][j - 1];
}

int lca(int u, int v) {
    if(deep[u] < deep[v]) swap(u, v);
    int dc = deep[u] - deep[v];
    for(int i = 0; i <= 19; i++) {
        if((1 << i) & dc) u = pre[u][i];
    }
    if(u == v) return u;
    for(int i = 19; ~i; i--) {
        if(pre[u][i] != pre[v][i]) {
            u = pre[u][i];
            v = pre[v][i];
        }
    }
    return pre[u][0];
}

int main() {
    scanf ("%d", &n);
    for (int i = 1, u, v, w; i <= n - 1; i++) {
        scanf ("%d %d %d", &u, &v, &w);
        ++u, ++v;
        addedge(u, v, w), addedge(v, u, w);
    }
    dfs(1, -1);
    solve();
    scanf ("%d", &q);
    while (q--) {
        scanf ("%d %d", &x, &y);
        ++x, ++y;
        printf ("%d\n", dis[x] + dis[y] - 2 * dis[lca(x, y)]);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ChaseNo1/p/11366608.html

时间: 2024-10-25 14:10:39

第三次周赛D题的相关文章

CSDN 轻松周赛赛题:能否被8整除

轻松周赛赛题:能否被8整除 题目详情 给定一个非负整数,问能否重排它的全部数字,使得重排后的数能被8整除. 输入格式: 多组数据,每组数据是一个非负整数.非负整数的位数不超过10000位. 输出格式 每组数据输出一行,YES或者NO,表示能否重排它的全部数字得到能被8整除的数.注意: 重排可以让0开头. 答题说明 输入样例   610 122 输出样例   YES NO 解释   第一个数可以变为016 , 160 解题:很水的一道题...思路很简单,1000是能被8整除的,所以一千的倍数都能被

2018 HDU多校第三场赛后补题

2018 HDU多校第三场赛后补题 从易到难来写吧,其中题意有些直接摘了Claris的,数据范围是就不标了. 如果需要可以去hdu题库里找.题号是6319 - 6331. L. Visual Cube 题意: 在画布上画一个三维立方体. 题解: 模拟即可. 代码: #include <bits/stdc++.h> using namespace std; int a, b, c, R, C; char g[505][505]; int main () { int T; cin >>

随机产生三十道四则运算题程序

#include<stdio.h>#include<stdlib.h>#include<time.h>int main(){ int x,y,z,t,i; srand(time(NULL)); printf("三十道四则运算题\n"); for(i=0;i<30;i++) { x=rand()%100; y=rand()%100; z=rand()%4; switch(z) { case 0: printf("%d+%d=\n&quo

CSDN轻松周赛赛题:能否被8整除

题目意思: 给定一个非负整数,问能否重排它的全部数字,使得重排后的数能被8整除.输入格式:多组数据,每组数据是一个非负整数.非负整数的位数不超过10000位.输出格式每组数据输出一行,YES或者NO,表示能否重排它的全部数字得到能被8整除的数.注意: 重排可以让0开头. 题目分析: 判断一个数是否能够被8整除,只需要判断这个数的后三位是否能够整除8即可,对于此题需要模拟判断所有的后三位数重排的六个数是够被8整除,只是注意一位数和两位数的时候需要自己判断. AC代码: #include<cstdi

第三次周赛题解【并查集 KMP DFS BFS 快速幂】

问题 A: 一道签到题 时间限制: 2 Sec  内存限制: 128 MB 提交: 63  解决: 28 [提交][状态][讨论版] 题目描述 我想说这是一道签到题,意思就是本次测试中最水的一道,不过我这样说你真的愿意相信我吗?哈哈,题目是这样的给你一下小数,然后请告诉我分别告诉我这个小数的循环节的循环次数.循环节以及循环节长度 输入 输入包括多组测试数据每组测试数据1行,包括一个小数,小数的长度不超过200,小数大于0小于100 输出 分别输出这个小数的循环节的长度.循环节以及循环次数,中间以

轻松周赛赛题:能否被8整除

练习题链接:http://student.csdn.net/mcs/programming_challenges 给定一个非负整数,问能否重排它的全部数字,使得重排后的数能被8整除. 输入格式: 多组数据,每组数据是一个非负整数.非负整数的位数不超过10000位. 输出格式 每组数据输出一行,YES或者NO,表示能否重排它的全部数字得到能被8整除的数.注意: 重排可以让0开头 今天一个小学弟问我这道题怎么做,我直接回答他将数字全排列,验证是否可被8整除,例如123的全排列:123.231.312

UVA_11877.第三次比赛C题:The Coco Cola

题目: 有一个coco cola小店,你可以用三个空瓶换一瓶coco cola饮料,如果你有n个空瓶,你可以喝到多少瓶饮料? 输入输出要求: intput:最多有10组案例,每组输入一个整数,0不被处理操作. Output:每组案例输出你能喝到的饮料数. sample intput: 3 10 81 0 sanple output: 1 5 40 代码如下: #include<iostream> #include<cstdio> using namespace std; int m

ACM训练联盟周赛 C题 Alice和Bob的Nim游戏

题目描述 众所周知,Alice和Bob非常喜欢博弈,而且Alice永远是先手,Bob永远是后手. Alice和Bob面前有3堆石子,Alice和Bob每次轮流拿某堆石子中的若干个石子(不可以是0个),拿到所有石子中最后一个石子的人获胜.这是一个只有3堆石子的Nim游戏. Bob错误的认为,三堆石子的Nim游戏只需要少的两堆的石子数量加起来等于多的那一堆,后手就一定会胜利.所以,Bob把三堆石子的数量分别设为 {k,4k,5k}(k>0). 现在Alice想要知道,在k 小于 2^n 的时候,有多

FJUT2019暑假第二次周赛 A题

# A题 服务器维护 TimeLimit:1500MS MemoryLimit:128MB 64-bit integer IO format:%lld Problem Description Dsc最大的梦想就是有一款属于自己的游戏(不可能的),可以把自己的奇思妙想在虚拟的世界中创造出来,假设Dsc成功了,创造出了一款网游(小作坊的那种),现在为了节省成本,Dsc决定自己维护服务器健康,但总会有没时间的时候. 假设从S分钟开始,E分钟结束[S,E]这段时间Dsc是没有时间的,这时候Dsc需要找人