zoj 3649 树上的倍增法

树上倍增 对于在树上查找某点的祖先,如果简单的查找时间复杂度是o(n),那么,我们可以通过倍增算法优化这个时间复杂度,将每次跳一个点变成,每次跳2的i次方个点,也就是倍增优化这个过程,将时间复杂度变为o(logn)。 如何进行倍增? 首先我们需要进行一遍dfs初始化,处理出每个点的深度。根据深度,初始化fa[now][i],fa数组存的是now节点的2得i次方倍祖先。 这样处理完成后,我们来看树上倍增... 阅读详情

Social Net

Time Limit: 5 Seconds      Memory Limit: 65536 KB

There are n individuals(2 <= n <= 30000). Everyone has one or more friends. And everyone can contact all people by friend-relation. If two persons aren't friends, they also can contact by their friends. Each pair of friends have a friendship value ai(1 <= ai <= 50000).

Firstly, you will relieve some friend-relation. The rest of the friend-relation is the social net. The net is unique in all test cases. In this net, everyone can contact all people by rest friend-relation. The net has a minimum number of friend-relation. And the net has maximum sum of friendship value. We want to get the maximum sum.

Secondly, everyone has an angry value bi(1 <= bi <= 100000). We have q operations(1 <= q <= 30000): Person X wants to contact person Y, this operation merely has one sequence which describes the process. The sequence consists of persons' angry value. The persons are on the process.

We suppose the sequence is c1, c2, c3, ... ,ci. Here ci means the angry value of the ith people in the sequence.

We attempt to find the maximum ck-cj (ck >= cj, j <= k).

Example:

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 4, 11(Y). The maximum ck-cj is 11-3=8.

The sequence is 3(X), 4, 5, 6, 7, 5, 9, 2, 11(Y). The maximum ck-cj is 11-2=9.

The sequence is 3(X), 10, 2, 5(Y). The maximum ck-cj is 10-3=7.

Input

The input contains multiple test cases. Each test case begins with a line containing a single integer n. The following line contains n integers bi.

The subsequent line describe the number of relations m(n <= m <= 50000). The next m lines contain the information about relations: x, y, ai. Their friendship value is ai.

Afterward gives q. The next q lines contain the operations: x, y. person X wants to contact person Y.

Output

For each case, print maximum sum of friendship value of the net on the first line.

The next q lines contain the answers of every operations.

Sample Input
6
3 5 1 7 3 5
7
1 2 5
1 3 6
2 4 7
2 5 8
3 6 9
4 5 1
5 6 2
5
6 1
6 2
6 3
6 4
6 5
Sample Output
35
2
4
0
6
4

第一问求最小生成树模板。第二问,每个节点有个ci值,查询树上节点u到v路径上(经过LCA)的点ci-cj(i>j)的最大值。

第二问麻烦就在于是树上的查询。先考虑简单的情形,假如生成的树是一条链,就变成rmq问题了,查询区间[l,r] ci-cj(i>=j)的最大值, 最大值要么是i<=m在[l,m]区间上,要么是j>m在[m+1,r]上,要么就是i>m,j<=m,在中间上等于右边区间最大值减去左边区间的最小值。所以假如用线段树之类rmq算法,线段树节点应该维护三个值,节点所代表的md[i]-ci-cj最大值,mi[i]-节点区间的最小值,mx[i]-节点区间的最大值。

但是线段树并不能直接用在树上,而还有一个rmq算法就是st算法,其原理就是倍增法,利用了二进制的思想。其实倍增法求树的最近公共祖先也类似于区间查询,知道了[l,m]的祖先(par[i][k])和[m+1,r]的祖先(par[par[i][k]][k]),那么就知道了[l,r]的祖先(par[i][k+1]),par[i][k]代表第i个节点的第2^k个祖先。

所以这道题需要维护的就是md[i][k],mdd[i],mi[i][k],mx[i][k],分别代表第i个节点开始到其第2^k个祖先这段序列的最大ci-cj值,最大cj-ci值,最小ci值,最大ci值。这里md[i]和mdd[i],一个是从下往上走,一个是从上往下走,因为这道题需要考虑走的方向,比如查询(u,v)和(v,u)方向是刚好相反的,所以要维护两个方向的。

但是树上的倍增法和st算法一样,支持区间查询操作但无法支持修改操作。其中对应的区间查询应该满足知道[l,m]和[m+1,r]的值,就能O(1)求的[l,r]对应的值。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
#define maxn 30005
#define maxv 16
#define inf 0x3f3f3f3f
typedef pair<int, int> p;
struct edge;
vector<edge> g[maxn];
int md[maxn][maxv], mdd[maxn][maxv], mi[maxn][maxv], mx[maxn][maxv];
int par[maxn][maxv];
int c[maxn], d[maxn];
int n,m,M;

struct edge
{
    int u,v,w;
    edge(){}
    edge(int uu, int vv, int ww){ u=uu;v=vv;w=ww;}
    bool operator <(const edge &a) const
    {
        return w>a.w;
    }
};
edge e[2*maxn];
int father[maxn]; //并查集用于kruskal
int find(int u)
{
    return u==father[u]?u:(father[u]=find(father[u]));
}

void unite(int u, int v)
{
    u=find(u);
    v=find(v);
    father[v]=u;
}

int vis[maxn];
void dfs(int u, int p, int dep)
{
    vis[u]=1;
    d[u]=dep;
    par[u][0]=p;
    if(p==-1){
        md[u][0]=0;
        mdd[u][0]=0;
        mi[u][0]=mx[u][0]=c[u];
    }
    else{
        md[u][0]= max(0, c[p]-c[u]);
        mdd[u][0]=max(0, c[u]-c[p]);
        mi[u][0]=min(c[p], c[u]);
        mx[u][0]=max(c[p], c[u]);
    }
    int v,w;
    for(int i=0; i<g[u].size(); i++){
        v=g[u][i].v, w=g[u][i].w;
        if(vis[v]) continue;
        dfs(v, u, dep+1);
    }
}

void init()
{
    for(int i=0; i<=n; i++) father[i]=i, g[i].clear();
    for(int i=1; i<=n; i++) scanf("%d", c+i);
    scanf("%d", &m);
    for(int i=0; i<m; i++)
        scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
    sort(e, e+m);

    int cnt=0;
    int res=0;
    for(int i=0; i<m && cnt<n-1; i++){  //kruskal最小生成树
        int u=e[i].u, v=e[i].v, w=e[i].w;
        if(find(u)==find(v)) continue;
        g[u].push_back(edge(u, v, w));
        g[v].push_back(edge(v, u, w));
        unite(u, v);
        res+=w;
        cnt++;
    }
    printf("%d\n", res);

    memset(vis, 0, sizeof(vis));
    for(int i=1; i<=n; i++)
        if(!vis[i])
        dfs(i,-1, 0);

    M=0;
    for(int i=1; i<=n; i++)
        M=max(d[i], M);
    M=ceil(log(M)/log(2));
    for(int v=1; v<=M; v++)
    for(int i=1; i<=n; i++){//初始化
       if(par[i][v-1]==-1){
            par[i][v]=-1;
            md[i][v]=md[i][v-1];
            mdd[i][v]=mdd[i][v-1];
            mx[i][v]=mx[i][v-1];
            mi[i][v]=mx[i][v-1];
       }
       else{
            int p=par[i][v-1];
            par[i][v]=par[p][v-1];
            mx[i][v]=max(mx[i][v-1], mx[p][v-1]);
            mi[i][v]=min(mi[i][v-1], mi[p][v-1]);
            md[i][v]=max(max(md[i][v-1], md[p][v-1]) ,mx[p][v-1]-mi[i][v-1]);
            mdd[i][v]=max(max(mdd[i][v-1], mdd[p][v-1]), mx[i][v-1]-mi[p][v-1]);
       }
    }
}

int lca(int u, int v)//求出最近公共祖先
{
    if(d[u]>d[v]) swap(u,v);

    for(int k=0; k<=M && d[v]!=d[u]; k++){
        if( (d[v]-d[u])>>k&1)
        v=par[v][k];

    }

    if(u==v) return u;
    for(int k= M; k>=0; k--){
        if(par[u][k] != par[v][k])
            u=par[u][k], v=par[v][k];
    }

    return par[u][0];
}

//md代表从下往上区间的最大差值,mdd代表从上往下的区间最大差值
//u,lca,v。假如u在v的左边。
//则需要查询(u,lca)的最大md值,和(lca,v)的最大mdd值,和((lca,v)的最大值-(u,lca)的最小值)。
p getmd(int u, int v) 
{
    if(u==v) return p(0, inf);
    int res=0, mini=c[u];
    for(int k=0; k<=M && d[v]!=d[u]; k++){
        if( (d[u]-d[v])>>k &1){ //利用二进制原理,比如往上走3步==00000011,等走2^0+2^1步
            res=max(md[u][k], res);
            res=max(res, mx[u][k]-mini);
            mini=min(mi[u][k], mini);
            u=par[u][k];
        }
    }

    return p(res, mini);  //返回最大md值,和区间最小值
}

p getmdd(int u, int v)
{
    if(u==v) return p(0, -inf);

    int res=0, maxi=c[u];
    for(int k=0; k<=M && d[v]!=d[u]; k++){
        if( (d[u]-d[v])>>k &1){
            res=max(mdd[u][k], res);
            res=max(res, maxi-mi[u][k]);
            maxi=max(maxi, mx[u][k]);
            u=par[u][k];
        }
    }

    return p(res, maxi);
}

int main()
{
    while(scanf("%d", &n)==1){
        init();
        int q;
        scanf("%d", &q);
        int u,v;
        while(q--){
            scanf("%d%d", &u, &v);
            int anc=lca(u,v);
            p t1=getmd(u, anc);
            p t2=getmdd(v, anc);
            int ans=0;
            
            ans=max(t1.first, t2.first);
            ans=max(ans, t2.second-t1.second);
            printf("%d\n", ans);

        }
    }
    return 0;
}


GAMES101:MacOS作业环境搭建 查看gcc/clang的位置产看gcc/clang的版本1.1.2 安装cmake。 阅读详情

相关推荐

海量数据 / Vastbase 深度研究笔记 v3.1

v3.1 重点修订:在 v3.0 审慎版基础上,新增“2026 新变量”章节,纳入 7 亿级定增、HTAP / 多模态时序数据库、G100 V3.0 Build 8 的 3CM+2DN 高可用补强、pgvector / V100 / AI 多模叙事,以及集成服务基因与标准软件 ARR 的拉扯。核心关注点是:Vastbase 能否把 openGauss 生态红利、数据库产品矩阵和行业交付能力,转化为可持续、可复购、低边际成本的标准软件收入。海量不是没有业务基本盘,也不是马上失血致死;客户越多,服务债越大。

喝醉酒的小白 544

树上倍增求链上最大值或最小值

倍增求链上点最大值 inline int LCA(int x,int y) { int maxn=max(w[x],w[y]); if (deep[x]>deep[y]) swap(x,y); int k=log2(deep[y]); for (int i=k;i>=0;i--) if (deep[f[i][y]]>=deep[x...

weixin_30527323的博客 730

Shell编程规范及变量

文章目录一.Shell脚本1.1Shell概述1.2Shell脚本 一.Shell脚本 1.1Shell概述 Shell是Linux系统中开机自动加载的一个特殊程序,它将用户输入的指令,转换成Linux内核可以识别的语言,从而让内核执行调用硬件。 Bash是Linux系统中默认使用shell的程序,它位于/bin/bash。 1.2Shell脚本 将需要执行的命令保存到一个文件中,按照顺序执行,它不需要编译,它是解释型的 ...

夏颜的博客 3673

树上倍增法

最近公共祖先

实践求真知 1333

YOLO11改进|注意力机制篇|引入ELA注意力机制

YOLO11添加ELA注意力机制

A1983Z的博客 3647

Plecs电力电子仿真专业教程-第一季 第一节 Plecs简介

Plces 电力电子仿真专业教程系列依托专业的电力电子仿真软件Plecs对各种电力电子常用的拓扑及实际设计的方案进行系统及仿真与软件代码仿真实践。通过对各种电路仿真原理的讲解及专业C语言代码程序的编写,提升学员的系统设计能力,软件编程能力与理论分析能力。

3640

图论算法:树上倍增法解决LCA问题(例题+cpp)

就是把每个 节点的 第 2^j 个的祖先找出来,用于之后的处理,同时我们还需要记录每个节点的。走一条边规定为走了一步,j可以表示为 0 ,1,2 ,分别代表走了 1步,2步,4步。一样,二分是缩小范围的,而倍增是扩大的,因此倍增与二分都具有。可以发现倍增是呈 2的指数型递增的一类数据,和。的时间复杂度,对于求解某些问题是非常高效的。,分别为1:节点6;2:节点5,4:节点1。,我们采用递归的形式,每次递归,走了四步:超过了范围,因此。走了一步: 到达了节点6。走了两步: 到达了节点5。什么是树的公共祖先?

ylh的博客 1766

沉降预测算法-指数曲线法

沉降预测的指数曲线法原理和代码实现

yh523的博客 1009

02 最近公共祖先(LCA)树上倍增法(附代码实现模板)

最近公共祖先(LCA) 最近公共祖先解决的问题 给定一颗有根树,若节点z即是节点x的祖先,也是节点y的祖先,则称z是x,y的公共祖先。 在x,y的所有公共祖先中,深度最大(距离根节点的距离最远的)的一个称为x,y的最近公共祖先,记作LCA(x,y)。 LCA(x,y)是x到根的路径与y到根的路径的交会点。它也是x与y之间的路径上深度最小的节点。求最近公共祖先的方法通常有以下三种。 方法1 向上标记法 从 x 向上走到根节点,并标记所有经过的节点。 从y向上从到根节点,当第一次遇到已经标记的节点时,就找到了

q2090988808的博客 1285

华为OD机试双机位C卷-简单的解压缩算法(C/C++/Py/Java/Js/Go)

华为OD机试(机考)双机位C卷 【简单的解压缩算法】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD真题【简单的解压缩算法】。

qq_45776114的博客 474

[Python] 如何使用 Python 调用 Dify 工作流服务实现自动化翻译

本文介绍了如何使用Python调用Dify的API实现文本翻译自动化。Dify作为AI开发平台,其工作流功能可执行文本处理、翻译等任务。选择Python调用Dify的优势在于职责划分清晰:Dify专注AI服务,Python处理文件操作等非AI任务。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 1095

深度剖析倍增算法求解最近公共祖先(LCA)的细枝末节

LCA的求解算法较多,本文详细介绍了倍增算法解决LCA问题中的细枝末节。

y6123236的专栏 729

自定义View系列教程04--Draw源码分析及其实践

通过之前的详细分析,我们知道:在measure中测量了View的大小,在layout阶段确定了View的位置。 完成这两步之后就进入到了我们相对熟悉的draw阶段,在该阶段真正地开始对视图进行绘制。按照之前的惯例,我们来瞅瞅View中draw( )的源码 public void draw(Canvas canvas) { final int privateFlags = mPriv

谷哥的小弟 2万+

zoj 3649 lca 倍增 DP

下课后直接奔实验室,月赛都快结束了,我擦,随便挑了一道题开始搞起来,是我喜欢的图论,心里暗暗欣喜,可是还是有点绕人,比赛结束刚好敲完,吃晚饭后,调了调,AC,呵呵 比赛的时候A的貌似很少,其实也不太难 链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3649 下面是我做过的倍增法求LCA以及倍增的DP的两个练习题

努力奋斗才能梦想成真 2076

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

ZOJ 3649 Social Net

前言 这题鸽了有一段时间了,今天上午自己想了想,冒出了一个不错的思路,而且似乎和之前听做过的同学讲的不太一样。然后就开始敲了,编译完马上过样例。交上去WA,TLE,各种错误接连而至。一直到刚刚,才结束了半天多的Debug,发现是快读打挂了…(逃…) 这是一个极其惨痛的教训。 题意 给你有N个节点的树,每个点都有一个权值。有Q个询问,每次询问从一个点走到另一个点,问你走过节点的序列c1,...

CaptainSlow 339

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

ZOJ 3649 Social Net(树上倍增)

一整天基本上只打了两题,每题半天,调起来简直爽爆。 There are n individuals(2 &lt;= n &lt;= 30000). Everyone has one or more friends. And everyone can contact all people by friend-relation. If two persons aren’t friends, they...

xyyxyyx的博客 271

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

最近公共祖先(LCA)之树上倍增法

最近公共祖先对于有根树T的两个结点u,v,最近公共祖先LCA(T,u,v)表示一个节点x,满足x是u,v的祖先并且x的深度尽可能大。 也可以把T理解为一个无向无环图,而LCA(T,u,v)即u到v的最短路上森度最小的点所以,LCA主要就是求解当两个点仅有唯一一条确定的最短路径时的路径信息的问题。树上倍增如果我们直接一个一个结点遍历去寻找这样的祖先,复杂度是O(n)的。而在算法竞赛中,经常会有个数较

Feynman1999的博客 976

树上倍增的写法和应用(详细讲解,新手秒懂)

最近做了一些树上的练习题,发现倍增真的是一种处理树上问题的神奇、方便的方法。 我以前一直打树链剖分打得多,但是学了倍增之后就再也不想打树链剖分了(当然有些题目不得不打)。 倍增比起树链剖分,代码短,容易查错,时空复杂度也优很多(nlogn),只是功能有些欠缺。 倍增的思想是二进制。         首先开一个n×logn的数组,比如fa[n][logn],其中fa[i][j]表示i

Saramanda的博客 1万+

最近公共祖先之树上倍增求法

一、问题引入 最近公共祖先(LCA)是求有根树上两点的深度最低的祖先节点,如下图,点5和点2的最近公共祖先为点4,点5和点3的最近公共祖先为点1,点5和点1的最近公共祖先为点1。 二、朴素算法 知道LCA定义后考虑最暴力的求解方法,可以先让在下面的点一层一层往上爬,直到两点具有相同深度(高度),之后两点同时一层一层往上爬,直到两点相同,这个相同的点就是最近公共祖先,若有m次询问,该算法时间复杂度为O(nm),显然过于暴力了。 三、树上倍增算法 朴素算法中找祖先节点都是一层一层地找,这样

Cloth的博客 680

最近公共祖先LCA算法——倍增法实现

什么是最近公共祖先?权威定义:对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。简而言之:在一棵没有环的树上,两个节点向上追溯,首次相遇的节点位置,就是这两个节点的最近公共祖先文字不好描述,下面以图来说明:如图所示,在左图中,节点4和节点6的最近公共祖先是节点1,在右图中,节点6和节点7的最近公共祖先是节点3。

qq_71450730的博客 1631

[zoj3649]Social Net

here are n individuals(2 <= n <= 30000). Everyone has one or more friends. And everyone can contact all people by friend-relation. If two persons aren’t friends, they also can contact by their friends.

Rec.的博客 467

ZOJ 3649 倍增法DP、树链剖分、tarjan并查集

Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit Status Practice ZOJ 3649 Description There are n individuals(2 n <= 30000). Everyone has one or more

Mr.Phoebe的专栏 869

Social Net ZOJ - 3649

题意 给出一幅有n个点的连通图,求出它的最大生成树,之后对于m个询问,每个询问包括两个数ab,求出a到b的路径上的极差最大值(必须用后面的数来减前面的) 方法 首先求出最大生成树,之后找到LCA,即可由它得到路径,但是发现暴力扫整一条路径肯定会TLE,因而可以用倍增的方法来预处理 dad[i][j]表示i节点的第(1 &amp;lt;&amp;lt; j)的父节点(即父节点的父节点的父节点,重复(1&amp;lt...

yzyyylx的博客 408

ZOJ 3649

倍增法DP #include #include #include #define N 50100 #define POW 17 #define inf 1000000 using namespace std; int val[N]; int head[N],cnt; int p[N][POW],dep[N],mi[N][POW],mx[N][POW],dp[N][POW],dp1[N][PO

1292765944的专栏 950

RMQ ZOJ 3649 Social Net

Social Net     Time Limit: 5 Seconds      Memory Limit: 65536 KB     There are n individuals(2  n <= 30000). Everyone has one or more friends. And everyone can contact all people by friend-r

We Can 788

树上倍增的应用

树上倍增的应用 归根到底,树上倍增的应用就是求LCA RMQ + 树上深搜 int fa[N][20]; int toUsed = 1; struct node{ int to; int nxt; }p[N*2]; void mkedge(int u,int v){ p[toUsed].to = v; p[toUsed].nxt = head[u]; ...

long1的博客 1286
上一篇: hdu 2255 二分图最大权匹配 km算法模板
下一篇: hdu 4738 边双连通&桥,有重边情况
HuanTongH
博客等级 码龄12年 7粉丝 110原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值