首页 > 其他分享 >2024信友队蓝润暑期集训提高1班②Day5

2024信友队蓝润暑期集训提高1班②Day5

时间:2024-07-16 15:57:21浏览次数:15  
标签:int 城市 Day5 最小 生成 2024 蓝润 mp1 push

知识总结

最小生成树

  • 最小生成树的定义:在一个无向连通图中,找出权值最小的生成树,使得生成树中任意两个顶点间都有且仅有一条路径。
  • 最小生成树的性质:
    • 无向连通图的最小生成树是唯一的。
    • 最小生成树的权值是图中所有边的权值的最小值。
    • 最小生成树的边数等于图的顶点数减一。
    • 最小生成树的边权值之和等于图的边权值之和。
  • 最小生成树的算法:
    • Prim算法:每次选择权值最小的边加入生成树,直到生成树的边数等于图的顶点数减一。
    • Kruskal算法:每次选择权值最小的边加入生成树,直到生成树的边数等于图的边数。

kruskal 重构树

  • 根据 kruskal 算法的思想
  • kruskal 重构树的叶子节点为原图中的点,其他店为虚点,点权为原图中的边权
  • 建 kruskal 重构树,维护每个子树的根到子树内的最短路,倍增查询满足条件的最小答案

题目

T1 电话连线(dial)

题目描述

一个国家有 n 个城市。若干个城市之间有电话线连接,现在要增加 m 条电话线,使得任意两个城市之间都直接或间接经过其他城市有电话线连接,你的程序应该能够找出最小费用及其一种连接方案。

思路解析

本题是一道很明显的最小生成树,原本是零的边保留下来,那么在做最小生成树的时候必定会将本来就有的边先用掉。当然也可以事先处理处联通块,更新图后进行最小生成树,至于使用克鲁斯科尔还是prim就随意了.两者复杂度分别是 $O(nnlognn)$ $O(n*nlogn)

代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf = 1e18, N = 1e3+10;
int n, ans, dis[N], mp[N][N], f[N][2], to[N];
bool vis[N];
void prim() {
    int minx, k = 1, tot = 0, cnt = 0;
    fill(dis, dis + N, inf);
    dis[1] = 0;
    for (int i = 1; i <= n; i++) {
        minx = inf;
        for (int j = 1; j <= n; j++) {
            if (dis[j] < minx && !vis[j]) {
                k = j;
                minx = dis[j];
            }
        }
        ans += minx;
        vis[k] = true;
        for (int j = 1; j <= n; j++) {
            if (mp[k][j] < dis[j] && !vis[j]) {
                dis[j] = mp[k][j];
                to[j] = k;
            }
        }
        if (minx) {
            f[cnt][0] = min(to[k], k);
            f[cnt][1] = max(to[k], k);
            cnt++;
        }
    }
    printf("%lld\n%lld", cnt, ans);
    return;
}
signed main() {
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%lld", &mp[i][j]);
        }
    }
    prim();
    return 0;
}

T2 安慰奶牛

题目描述

https://www.luogu.com.cn/problem/U183932

思路解析

考虑每条边都必须经过两次,转换每条边的边权为边权*2+两边的点权,跑一个最小生成树

代码实现

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5e6 + 5, M = 2e6 + 10, inf = LONG_LONG_MAX;
struct node {
    int u, v, w;
} e[M];
bool operator<(const node& x, const node& y) {
    return x.w < y.w;
}
int n, m, father[N], c[N], minn = inf, ans, num_edge;
int findfather(int v) {
    if (father[v] == v)
        return v;
    else {
        father[v] = findfather(father[v]);
        return father[v];
    }
}
void Kruskal() {
    for (int i = 1; i <= n; i++) {
        father[i] = i;
    }
    sort(e + 1, e + 1 + m);
    for (int i = 1; i <= m; i++) {
        int fu = findfather(e[i].u), fv = findfather(e[i].v);
        if (fu != fv) {
            ans += e[i].w;
            father[fu] = fv;
            num_edge++;
            if (num_edge == n - 1)
                break;
        }
    }
    return;
}

signed main() {
    scanf("%lld%lld", &n, &m);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &c[i]);
        minn = min(c[i], minn);
    }
    ans = minn;
    for (int i = 1; i <= m; i++) {
        scanf("%lld%lld%lld", &e[i].u, &e[i].v, &e[i].w);
        e[i].w = 2 * e[i].w + c[e[i].u] + c[e[i].v];
    }
    Kruskal();
    printf("%lld\n", ans);
    return 0;
}

T3 新的开始

题目描述

发展采矿业当然首先得有矿井, 小FF花了上次探险获得的千分之一的财富请人在岛上挖了n口矿井, 但他似乎忘记考虑的矿井供电问题……
为了保证电力的供应, 小FF想到了两种办法:
1、 在这一口矿井上建立一个发电站, 费用为v(发电站的输出功率可以供给任意多个矿井)。
2、 将这口矿井与另外的已经有电力供应的矿井之间建立电网, 费用为p。
小FF希望身为”NewBe_One" 计划首席工程师的你帮他想出一个保证所有矿井电力供应的最小花费。

思路解析

井建一个新点,每个点可以选择向井连边或者点与点之间连边,边权不同,求一个最小生成树即可

代码实现

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N 310
int n, m, i, j, k;
int v[N], b[N], a[N], p[N][N], ans;

signed main() {
    scanf("%lld", &n);
    a[k = 0] = 1e9;
    for (i = 1; i <= n; ++i) {
        scanf("%lld", &v[i]);
        a[i] = v[i];
    }
    for (i = 1; i <= n; ++i) {
        for (j = 1; j <= n; ++j) {
            scanf("%lld", &p[i][j]);
        }
    }

    for (i = 1; i <= n; ++i) {
        for (j = 1, k = 0; j <= n; ++j)
            if (!b[j] && a[j] < a[k])
                k = j;
        for (j = b[k] = 1, ans += a[k]; j <= n; ++j)
            if (!b[j])
                a[j] = min(a[j], p[k][j]);
    }
    printf("%lld", ans);
    return 0;
}

T4 过路费

题目描述

在某个遥远的国家里,有n个城市。编号为1,2,3,…,n。这个国家的政府修建了m条双向道路,每条道路连接着两个城市。政府规定从城市S到城市T需要收取的过路费为所经过城市之间道路长度的最大值。如:A到B长度为2,B到C长度为3,那么开车从A经过B到C需要上交的过路费为3。
佳佳是个做生意的人,需要经常开车从任意一个城市到另外一个城市,因此他需要频繁地上交过路费,由于忙于做生意,所以他无时间来寻找交过路费最低的行驶路线。然而,当他交的过路费越多他的心情就变得越糟糕。作为秘书的你,需要每次根据老板的起止城市,提供给他从开始城市到达目的城市,最少需要上交多少过路费。

思路解析

考虑最小生成树。
然后转换成 LCA 问题。

代码实现

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int f[N][21], g[N][21], dep[N], fa[N];
vector<pair<int, int> > e[N];
struct Edge {
    int u, v, w;
} eg[N];
bool cmp(Edge a, Edge b) {
    return a.w < b.w;
}
void addEdge(int x, int y, int z) {
    e[x].push_back(make_pair(y, z));
    e[y].push_back(make_pair(x, z));
}
int union_find(int x) {
    return fa[x] < 0 ? x : fa[x] = union_find(fa[x]);
}
void Kruskal(int n, int m) {
    for (int i = 1; i <= n; ++i) {
        fa[i] = -1;
    }
    sort(eg + 1, eg + m + 1, cmp);
    for (int i = 1, x, y; i <= m; ++i) {
        if ((x = union_find(eg[i].u)) != (y = union_find(eg[i].v))) {
            if (fa[x] <= fa[y])
                fa[x] += fa[y], fa[y] = x;
            else
                fa[y] += fa[x], fa[x] = y;
            addEdge(eg[i].u, eg[i].v, eg[i].w);
        }
    }
}
void dfs(int x, int fa) {
    f[x][0] = fa;
    for (int j = 0; f[x][j]; ++j)
        f[x][j + 1] = f[f[x][j]][j], g[x][j + 1] = max(g[x][j], g[f[x][j]][j]);
    for (auto v : e[x])
        if (v.first != fa)
            dep[v.first] = dep[x] + 1, g[v.first][0] = v.second, dfs(v.first, x);
}
int query(int x, int y) {
    int ans = 0;
    if (dep[x] < dep[y]) {
        int t = x;
        x = y;
        y = t;
    }
    for (int i = 0, d = dep[x] - dep[y]; i <= 20; ++i)
        if (d & (1 << i))
            ans = max(ans, g[x][i]), x = f[x][i];
    if (x == y)
        return ans;
    for (int i = 20; i >= 0; --i)
        if (f[x][i] != f[y][i])
            ans = max(ans, max(g[x][i], g[y][i])), x = f[x][i], y = f[y][i];
    ans = max(ans, max(g[x][0], g[y][0]));
    return ans;
}
int main() {
    int n, m, q;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= m; ++i)
        scanf("%d %d %d", &eg[i].u, &eg[i].v, &eg[i].w);
    Kruskal(n, m);
    dfs(1, 0);
    scanf("%d", &q);
    for (int i = 1, x, y; i <= q; ++i) {
        scanf("%d %d", &x, &y);
        printf("%d\n", query(x, y));
    }
    return 0;
}

T5 公路网问题

题目描述

  某省的公路网有一个简单的结构:以省会城市为起点,道路延伸到邻近的一些城市,从这些城市开始,又有一些道路扩展到次近城市,依此类推,因此,各城市可以被视为按"层次"环绕在省会城市周围的。第i层的城市只可能与第i-1层和第i+1层中的城市直接相连(省会城市被视为第0层 )。公路网中不会出现回路。任何第i层的城市只与i-1层的一个城市直接相连,但是可以与0到多个第i+1层的城市直接相连。因此,从一个指定的第i层的城市到省会城市,游客只需简单地顺唯一的一条路到达与它直接相连的第i-1层的城市,并重复这一步骤,这样每一步后都将更加接近省会城市,直至最后到达。

  对于一个给定的公路城市网,你的任务是找出给定两城市之间的最短通路,路径长度用中途经过的城市的数目衡量。

思路解析

很容易看出这是一棵树.
我们要求的就是树上的路径.
定义H[i]表示i的深度
假设是i点走到j点
可以很容易的想到,凡是H[i]>H[j]的时候,肯定我们先移动i不会有错误,反之依然.
当h[i] == h[j]的时候,我们将两个点同时上伸也不会有错.
所以这就是一个O(N)的模拟了.

代码实现

#include <iostream>
#include <map>
#include <queue>
#include <stack>
using namespace std;
int n, Q, ans, LCA, num, cnt, Fa[100001], deep[100001];  // Fa表示某城市的父亲城市(省会为根城市),deep表示某城市的深度
vector<int> V[100001];                                   // 存树用
map<string, int> mp1;                                    // 将某城市映射为一个数用
map<int, string> mp2;                                    // 将某城市映射为的那个数从新映射回某城市用
queue<int> num1;                                         // 输出用
stack<int> num2;                                         // 输出用
void Deep(int now, int fa)                               // 计算节点深度
{
    Fa[now] = fa;
    deep[now] = deep[fa] + 1;
    for (int i = 0; i < V[now].size(); i++)
        if (V[now][i] != fa)
            Deep(V[now][i], now);
}
int lca(int x, int y)  // 向上标记法求lca
{
    if (deep[x] > deep[y])
        swap(x, y);
    while (deep[x] != deep[y])
        y = Fa[y];
    while (x != y) {
        x = Fa[x];
        y = Fa[y];
    }
    // q.push(mp2[x]);
    return x;
}
void do1(int x) {
    if (x == LCA) {
        num1.push(x);
        return;
    }
    num1.push(x);
    do1(Fa[x]);
}
void do2(int x) {
    if (x == LCA)
        return;
    num2.push(x);
    do2(Fa[x]);
}
int main() {
    cin >> n >> Q;
    for (int i = 1; i <= n; i++) {
        // 存树
        string u, v;
        cin >> u >> v;
        // 相互转化
        if (!mp1[u]) {
            mp1[u] = ++cnt;
            mp2[cnt] = u;
        }
        if (!mp1[v]) {
            mp1[v] = ++cnt;
            mp2[cnt] = v;
        }
        V[mp1[u]].push_back(mp1[v]);
        V[mp1[v]].push_back(mp1[u]);
    }
    Deep(1, 0);                   // 计算深度
    for (int i = 1; i <= Q; i++)  // 求解
    {
        string x, y;
        cin >> x >> y;
        // q.push(x);
        // q.push(y);
        LCA = lca(mp1[x], mp1[y]);
        do1(mp1[x]);
        do2(mp1[y]);
        while (!num1.empty()) {
            cout << mp2[num1.front()];
            num1.pop();
        }
        while (!num2.empty()) {
            cout << mp2[num2.top()];
            num2.pop();
        }
        cout << ' ';
    }
    return 0;
}

初赛

image-20240713192446071

image-20240713192504383

image-20240713192518327

image-20240713192708892

image-20240713192741239

image-20240713192823169

image-20240713192934104

image-20240713193034600

标签:int,城市,Day5,最小,生成,2024,蓝润,mp1,push
From: https://www.cnblogs.com/TangyixiaoQAQ/p/18300515

相关文章

  • 2024信友队蓝润暑期集训提高1班②Day4
    知识总结搜索算法剪枝剪枝是指在搜索树的构造过程中,对某些分支不必继续探索,从而减少搜索树的大小,提高搜索效率。启发式搜索启发式搜索是指根据某种启发式函数对搜索树进行排序,使得搜索树中优先扩展那些有可能产生最优解的分支。迭代加深搜索迭代加深搜索是指在搜索树的构造......
  • [2024] springboot Hadoop技术下的校园二手交易系统的设计与实现
    博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数......
  • 2024最新版Python安装详细教程!一键安装,永久使用
    打开上面的Python官网地址,如下图所示,鼠标放入网页Downloads栏目,选择里面的windows操作系统。三、进入windows对应的页面,选择python版本(1)选择python的稳定发布版本StableReleases点击进入windows操作系统对应的页面,显示python安装版本,这些python安装版本适合windows操......
  • 2024-07-16 代码高亮插件highlight.js安装使用以及排错日志
    highlight.js—— 一个开源语法高亮库,用于在网页上对源代码进行语法高亮显示。安装npmihighlight.jsyarnaddhighlight.js引入//main.jsimport{createApp}from'vue';importAppfrom"./App.vue";importhljsfrom"highlight.js";//代码高亮插件import......
  • SMU Summer 2024 Contest Round 4
    SMUSummer2024ContestRound4MadeUp题意给你三个序列\(A,B,C\),问你满足\(A_i=B_{C_j}\)的\((i,j)\)对有多少。思路由于\(1\leA_i,B_i,C_i\leN\),所以可以统计\(Cnt[A_i]\)和\(Cnt[B_{C_i}]\)的个数,两者相乘累加即可。代码#include<bits/stdc++.h>using......
  • 2024-07-16 记录vue内置组件(ps:内容来自GPT)
    1. Transition和TransitionGroup用途:用于为单个元素或组件提供过渡效果。TransitionGroup则用于列表中的多个元素或组件的过渡效果。特点:Transition:只影响单个元素或组件,不会额外渲染DOM元素。TransitionGroup:渲染一个真实的DOM元素(默认为<span>),可以通过tag属性改变渲染......
  • 2024年7月JLPT日语N2真题试卷、答案解析、听力原文
    本套真题由【学日语的師夫】制作排版,分享下载日语等级考试N1N2N3N4N5专四专八历年真题PDF文件,树先生日语真题的平替内容,精讲版答案解析非常适合复习备考,听力原文真是还原听力场景,多听多练习。如果你正在备考12月份的考试,可以参考【学日语的師夫】排版的真题内容,刷真题是最有效......
  • 【2024】springboot校服订购系统设计与实现
     博主介绍:✌CSDN新星计划导师、Java领域优质创作者、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌技术范围:SpringBoot、Vue、SSM、HTML、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大......
  • StableDiffusion 安装部署教程,轻松上手无压力!(附2024最新SD安装包)
    亲爱的小伙伴们,大家好!今天要给大家分享一个超级实用的SD安装教程,让您轻松开启新的体验之旅!一、SD简介SD是一款功能强大且备受欢迎的软件/工具,它具有的以下功能和优势,能够为您的工作、学习和娱乐带来极大的便利。功能:1.文生图创作-根据输入的文本描述生成逼真或......
  • 【AI资讯早报】AI科技前沿资讯概览:2024年7月16日早报
    【AI资讯早报】AI科技前沿资讯概览,涵盖了行业大会、技术创新、应用场景、行业动态等多个方面,全面展现了AI领域的最新发展动态和未来趋势。1.人工智能标准化体系加速构建工业和信息化部等四部门联合发布了《国家人工智能产业综合标准化体系建设指南(2024版)》,旨在到2026年新......