题解 P1967 【货车运输】

本文介绍了一种使用树链剖分和线段树解决寻找两点间路径上边权最小值问题的方法。文章详细解释了如何通过构建最小生成树,将边权转化为点权,并使用树链剖分结合线段树来高效查询两点间路径上的最小边权。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树链剖分+线段树

思路

貌似题解里没有树链剖分和线段树的,贡献一发。

首先明确题目要求:一辆车走某条路从x城到y城的边权最小值

我们把要求分开来看:

  1. 从x城到y城:我们需要走的路径将两点联通

  2. 边权最小值:我们要找这条路上的限重最小值

如果你是一个货车司机(而且题目还告诉你你的汽车走多远不要油),你肯定想多运一些货物,也就要求联通两点的权值尽可能大。

又要保证联通,又要保证权值尽可能大,没错,我们需要用到最小生成树。

(如果还不理解,你可以设想一下,有两条都可以从a到b,一条路限重10,一条路限重100,你一定会选择第二条路;我们再推广一下,如果两条路都能联通还未联通的a、b两个联通块(你可以认为a、b是两个岛,两条路是跨岛大桥),一条路限重10,一条路限重100,你还是一定会选择第二条路)

最小生成树的方法:先按边权大小排序,利用并查集判断两块是否联通,生成一个新的图


好,现在第一个问题解决了:你运货的最大路径方案一定在新的图(树)上了,怎么求两点之间权值最小的呢?

因为这是一棵树,所以两点之间路径唯一,可是直接搜索时间又肯定承受不住,我们这时就可以采用树链剖分了

这是类似树剖板题的题,就有提到求某两点的最值问题

值得一提的是:树剖+线段树只是支持修改和查询点权的,这时我们就需要知道怎么将边权转换为点权

边权与点权之间的转换

timg?image&quality=80&size=b9999_10000&sec=1521015915358&di=c761d3150b4221a7eaa7dbe10521a558&imgtype=jpg&src=http%3A%2F%2Fimages0.cnblogs.com%2Fblog2015%2F790029%2F201507%2F281612511418262.png

随便在网上找了个图:我们这样实现边权与点权之间的转换:将根节点的点权设为INF,然后所有边权下放到连接的点(所有边权往下挪到了点里,由于根节点值为INF不影响min的计算(同理,查询最大值就设为-INF))

然后直接查询就好啦!

怎么可能?!

刚开始的时候,我转换完后就直接像树剖板题那样求最值了,结果只有10分,那么问题出在哪呢?

我们看一下这个图(黑色是边权,黄色是转换后的点权):

15596.png

若想查询A点到B点的最值,我们会发现,按普通树剖的查询方法,我们会访问20那个点(5-20-19-8),然而应该访问的路径是5-19-8,所以我们要对查询函数做一些修改,“绕开那些点”

void getans(int x,int y){
    if(findfather(x) != findfather(y)){
        printf("-1\n");
        return ;
        }
    int ans = INF;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])swap(x,y);
        ans = min(ans,query(1,pos[top[x]],pos[x]));
        x = fa[top[x]];
        }
    if(x == y){
        printf("%d\n",ans);//绕开
        return ;
        }
    if(dep[x] > dep[y])swap(x,y);
    ans = min(ans,query(1,pos[x] + 1,pos[y]));//+1绕开
    printf("%d\n",ans);
    }

AC代码

#include<iostream>
#include<vector>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int RD(){
    int out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const int maxn = 500190,INF = 999999999;
int num,nr,nume,na,cnt,numt;
int head[maxn];
struct Node{
    int v,nxt,dis;
    }E[maxn * 2];
void add(int u,int v,int dis){
    E[++nume].nxt = head[u];
    E[nume].v = v;
    E[nume].dis = dis;
    head[u] = nume;
    }
struct R{
    int u,v,dis;
    }I[maxn];
bool cmp(R a,R b){
    return a.dis > b.dis;
    }
int father[maxn];
int findfather(int v){
    if(father[v] == v)return v;
    return father[v] = findfather(father[v]);
    }
void Union(int a,int b){
    int faA = findfather(a);
    int faB = findfather(b);
    if(faA != faB)father[faA] = faB;
    }
void buildG(){//建最小生成树
    for(int i = 1;i <= nr;i++){
        if(findfather(I[i].u) != findfather(I[i].v)){
            add(I[i].u,I[i].v,I[i].dis);
            add(I[i].v,I[i].u,I[i].dis);
            Union(I[i].u,I[i].v);
            }
        }
    }
int dep[maxn],fa[maxn],wson[maxn],top[maxn],size[maxn],pos[maxn],ori[maxn];
int val[maxn];
int vis[maxn];
void dfs1(int id,int F){
    vis[id] = true;
    numt++;
    size[id] = 1;
    for(int i = head[id];i;i = E[i].nxt){
        int v = E[i].v;
        if(v == F)continue;
        dep[v] = dep[id] + 1;
        fa[v] = id;
        val[v] = E[i].dis;
        dfs1(v,id);
        size[id] += size[v];
        if(size[v] > size[wson[id]]){
            wson[id] = v;
            }
        }
    }
void dfs2(int id,int TP){
    top[id] = TP;
    pos[id] = ++cnt;
    ori[cnt] = id;
    if(!wson[id])return ;
    dfs2(wson[id],TP);
    for(int i = head[id];i;i = E[i].nxt){
        int v = E[i].v;
        if(v == fa[id] || v == wson[id])continue;
        dfs2(v,v);
        }
    }
#define lid (id << 1)
#define rid (id << 1) | 1
struct sag_tree{
    int l,r;
    int min;
    int lazy;
    }tree[maxn << 2];
void build(int id,int l,int r){
    tree[id].l = l;
    tree[id].r = r;
    if(l == r){
        tree[id].min = val[ori[r]];
        return ;
        }
    int mid = l + r >> 1;
    build(lid,l,mid);
    build(rid,mid + 1,r);
    tree[id].min = min(tree[lid].min,tree[rid].min);
    }
int query(int id,int l,int r){
    if(tree[id].l == l && tree[id].r == r){
        return tree[id].min;
        }
    int mid = tree[id].l + tree[id].r >> 1;
    if(mid < l){
        return query(rid,l,r);
        }
    else if(mid >= r){
        return query(lid,l,r);
        }
    else{
        return min(query(lid,l,mid),query(rid,mid + 1,r));
        }
    }
void getans(int x,int y){
    if(findfather(x) != findfather(y)){
        printf("-1\n");
        return ;
        }
    int ans = INF;
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]])swap(x,y);
        ans = min(ans,query(1,pos[top[x]],pos[x]));
        x = fa[top[x]];
        }
    if(x == y){
        printf("%d\n",ans);
        return ;
        }
    if(dep[x] > dep[y])swap(x,y);
    ans = min(ans,query(1,pos[x] + 1,pos[y]));
    printf("%d\n",ans);
    }
int main(){
    num = RD();nr = RD();
    for(int i = 1;i <= num;i++){
        father[i] = i;
        }
    for(int i = 1;i <= nr;i++){
        I[i].u = RD();
        I[i].v = RD();
        I[i].dis = RD();
        }
    sort(I + 1,I + 1 + nr,cmp);
    buildG();
    int s = 1;
    while(s <= num){
        if(vis[s] == false){
            dep[s] = 1;
            val[s] = INF;
            dfs1(s,-1);
            dfs2(s,s);
            }
        s++;
        }
    build(1,1,numt);
    na = RD();
    int u,v;
    for(int i = 1;i <= na;i++){
        u = RD();v = RD();
        getans(u,v);
        }
    return 0;
    }

最后,感谢大佬的帮助
大佬

广告

转载于:https://www.cnblogs.com/Tony-Double-Sky/p/9283260.html

<think>好的,用户需要寻找洛谷P8602问题的C语言题解和实现方法。首先,我得确认P8602的具体题目内容。但根据用户提供的引用,目前只有引用[1]和引用[2],分别对应P1980和P2669的题解。用户提到的P8602可能不在当前提供的引用中,所以可能需要根据题目编号推断或查找相关信息。 不过,考虑到用户可能没有正确输入题目编号,或者希望我基于现有的引用内容进行推测。但假设用户确实需要P8602的题解,而目前引用中没有相关的内容,我应该如何应对呢? 首先,可能需要询问用户是否确认题目编号正确,或者提供更多题目细节。但由于用户明确请求帮助寻找C语言实现,可能需要根据常见的算法题类型进行推测。例如,P8602可能涉及某种算法,如动态规划、贪心、搜索等。 另外,用户可能希望得到类似引用中提供的题解结构,即包括代码示例、算法思路和相关问题。因此,需要结合常见的C语言题解结构来组织回答。 例如,假设P8602是一个计数问题或数学问题,可以参考引用中的处理方式,如循环、数组操作等。如果题目涉及特定的数学公式,可能需要应用相应的算法。 另外,用户提到系统级指令要求生成相关问题,因此需要在回答后添加相关问题部分。同时,要确保使用中文回答,并遵循LaTeX格式要求。 可能的步骤: 1. 确认题目内容:由于引用中没有P8602的信息,可能需要假设题目类型,或者提示用户提供更多信息。 2. 若无法确认,可提供一个通用的问题解决框架,例如分析问题、设计算法、编写代码。 3. 生成相关问题,如优化方法、应用场景等。 但根据用户给出的示例,他们可能希望得到具体的代码实现,如引用中的示例。因此,可能需要构造一个假设的题目,并给出对应的C语言解法。不过这可能不准确,因为实际题目可能假设不同。 或者,可能用户误将P8602现有引用中的问题混淆,需要进一步澄清。但由于无法获取外部信息,只能基于现有引用内容进行回答。 因此,可能的回答策略是:说明当前引用中没有P8602的题解,但可提供一般性的解题步骤,并生成相关问题。或者,假设P8602是一个类似计数或数学问题,并基于引用中的方法给出示例。 例如,若P8602类似于P1980的计数问题,可能需要遍历数字并统计特定条件。代码可能涉及循环和模运算。或者,若涉及金币累积问题,类似P2669,使用双重循环累加。 最终,可能需要告知用户当前信息不足以提供准确题解,并建议提供更多题目细节。同时,根据现有引用,给出可能的解题思路,并生成相关问题。</think>由于未获取到洛谷P8602题目的具体描述,我将根据常见算法题类型和您提供的其他题解特征,给出通用解题框架及类似问题的实现思路。若需精确解答,请补充题目描述。 ### 通用解题框架(以数值处理类问题为例) 1. **问题分析**:确认输入输出格式、数值范围限制 2. **算法设计**:根据问题特征选择暴力枚举/数学公式/动态规划等算法 3. **优化策略**:通过时间复杂度分析进行剪枝或优化 4. **边界处理**:特别注意0值、极值等特殊情况的处理 ### 示例代码模板(以数字统计类问题为例) ```c #include <stdio.h> int main() { int n, target, count = 0; scanf("%d%d", &n, &target); for(int i = 1; i <= n; i++) { int num = i; while(num > 0) { if(num % 10 == target) { count++; } num /= 10; } } printf("%d", count); return 0; } ``` ### 典型问题解法特征 1. **数值遍历**:使用循环结构处理1到n的数值范围 2. **数位分解**:通过取余运算`%10`和整除运算`/10`分解数字 3. **计数策略**:使用计数器变量记录符合条件的情况[^1] 4. **边界处理**:特别注意0值的特殊处理逻辑[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值