2015年蓝桥杯省赛C/C++ A组 灾后重建题解(100分)

这篇具有很好参考价值的文章主要介绍了2015年蓝桥杯省赛C/C++ A组 灾后重建题解(100分)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

10. 灾后重建

Pear市一共有N(<=50000)个居民点,居民点之间有M(<=200000)条双向道路相连。这些居民点两两之间都可以通过双向道路到达。这种情况一直持续到最近,一次严重的地震毁坏了全部M条道路。
震后,Pear打算修复其中一些道路,修理第i条道路需要Pi的时间。不过,Pear并不打算让全部的点连通,而是选择一些标号特殊的点让他们连通。
Pear有Q(<=50000)次询问,每次询问,他会选择所有编号在[l,r]之间,并且 编号 mod K = C 的点,修理一些路使得它们连通。由于所有道路的修理可以同时开工,所以完成修理的时间取决于花费时间最长的一条路,即涉及到的道路中Pi的最大值。

你能帮助Pear计算出每次询问时需要花费的最少时间么?这里询问是独立的,也就是上一个询问里的修理计划并没有付诸行动。

【输入格式】
第一行三个正整数N、M、Q,含义如题面所述。
接下来M行,每行三个正整数Xi、Yi、Pi,表示一条连接Xi和Yi的双向道路,修复需要Pi的时间。可能有自环,可能有重边。1<=Pi<=1000000。
接下来Q行,每行四个正整数Li、Ri、Ki、Ci,表示这次询问的点是[Li,Ri]区间中所有编号Mod Ki=Ci的点。保证参与询问的点至少有两个。

【输出格式】
输出Q行,每行一个正整数表示对应询问的答案。

【样例输入】
7 10 4
1 3 10
2 6 9
4 1 5
3 7 4
3 6 9
1 5 8
2 7 4
3 2 10
1 7 6
7 6 9
1 7 1 0
1 7 3 1
2 5 1 0
3 7 2 1

【样例输出】
9
6
8
8

【数据范围】
对于20%的数据,N,M,Q<=30
对于40%的数据,N,M,Q<=2000
对于100%的数据,N<=50000,M<=2*10^5,Q<=50000. Pi<=10^6.
Li,Ri,Ki均在[1,N]范围内,Ci在[0,对应询问的Ki)范围内。

资源约定:
峰值内存消耗 < 256M
CPU消耗 < 5000ms

请严格按要求输出,不要画蛇添足地打印类似:“请您输入…” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include , 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型。

这题比较复杂,算法总共要分为三个部分。

首先,每次询问其实都是给出一个特定点集,要求最小化把这些点连通的边权的最大值。
那么,该问题是MST问题的变体 最小生成树资料
进一步地,对于每次询问,最佳方案的边都在原图的最小生成树中,可由反证法证得。
因此,算法的第一部分就是抛弃原图,只留下最小生成树,边数减少到 n − 1 n-1 n1,并且有很多好用的特征。
任选一点使之成为有根树,树上任意两点有且仅有一条简单路径,也即两点分别向上连到LCA 最近公共祖先资料

本题所取点集与除法取模有关,可以考虑 Big Small 分界。参考资料:fold的毒瘤题
设定一个阈值 T T T

  1. k > T k>T k>T时,点数约为 n k \frac{n}{k} kn,至多为 n T \frac{n}{T} Tn,顺序遍历所有点是可接受的。
    可以对LCA分类讨论证得,点1点3路径的最大值,其实已包含在点1点2路径和点2点3路径。
    因此,对于特定点集并不需要两两求LCA,只需要对“相邻”点顺序求过去就行。
    由于原图的MST不会变动,可以采用倍增预处理的方法作为算法的第二部分。
  2. k < = T k<=T k<=T时, k k k的取值至多有 T T T种,遍历不同的 k k k是可接受的。
    考虑到上述“相邻”的特性,其实对于同一组 ( k , c ) (k,c) (k,c),就是多次查询不同的 ( l , r ) (l,r) (l,r)区间,
    因为本题允许离线处理,可以把所有符合条件的点的路径建立数据结构,这是算法的第三部分。
    同样是没有修改,这里用线段树来优化查询速度 线段树资料

T T T n \sqrt n n 时,总体复杂度最小。

进一步分析发现,该方法还可以简化,下面说明只用线段树方法复杂度仍然是正确的。
考虑最极端情况,每次询问的 ( k , c ) (k,c) (k,c)均不同,每次都需要重新建树,因为 k k k越小点集越大,且对于每个 k k k c c c各有 k k k种取值,因此求LCA和建树的总复杂度为
T ( n ) = n 1 ( log ⁡ n + log ⁡ n 1 ) + ( n 2 ( log ⁡ n + log ⁡ n 2 ) ) × 2 + ( n 3 ( log ⁡ n + log ⁡ n 3 ) ) × 3 + … T(n) = \frac{n}{1}(\log n + \log \frac{n}{1}) + (\frac{n}{2}(\log n + \log \frac{n}{2})) \times 2 + (\frac{n}{3}(\log n + \log \frac{n}{3})) \times 3 + \dots T(n)=1n(logn+log1n)+(2n(logn+log2n))×2+(3n(logn+log3n))×3+
≤ n ⋅ 2 log ⁡ n + n ⋅ 2 log ⁡ n + n ⋅ 2 log ⁡ n + … \le n \cdot 2 \log n + n \cdot 2 \log n + n \cdot 2 \log n + \dots n2logn+n2logn+n2logn+
= O ( n ⋅ n ⋅ log ⁡ n ) = O(\sqrt{n} \cdot n \cdot \log n) =O(n nlogn)

查询的总复杂度显然是 O ( q ⋅ log ⁡ n ) O(q \cdot \log n) O(qlogn),两部分都完全没毛病。

本题从 Big Small 分界出发,到最后发现其实并不需要 Big Small 分界,直接建简化线段树的复杂度也是没有问题的,真是挺有趣的。

不过在线练习系统只给了1s的时限就比较紧,这就必须得套个读入优化才能保证每次都过了,读入量接近百万级(20w*3+5w*4)。

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

typedef pair<int, int> PII;
const int N = 50010;
const int M = 200010;
const int FN = 16;
vector<PII> G[N];
int dep[N], ans[N];
int fa[N][FN], val[N][FN];
struct Que {
    int x, l, r, k, c;
} que[N];
bool debug = false;

inline void getmax(int& x, int y)
{
    if (y > x)
        x = y;
}

namespace Kruskal {
int p[N], ra[N];
struct Edge {
    int u, v, w;
} eg[M];

int cmp(const Edge& p1, const Edge& p2) { return p1.w < p2.w; }

int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }

int merge(int x, int y)
{
    x = find(x);
    y = find(y);
    if (x == y)
        return 0;
    if (ra[x] > ra[y]) {
        p[y] = x;
    } else {
        if (ra[x] == ra[y])
            ra[y]++;
        p[x] = y;
    }
    return 1;
}

void build(int kn, int km)
{
    int cnt = 0;
    for (int i = 1; i <= kn; i++) {
        p[i] = i;
        ra[i] = 0;
    }
    sort(eg + 1, eg + km + 1, cmp);
    for (int i = 1; i <= km; i++) {
        if (merge(eg[i].u, eg[i].v)) {
            G[eg[i].u].push_back(PII(eg[i].v, eg[i].w));
            G[eg[i].v].push_back(PII(eg[i].u, eg[i].w));
            if (++cnt == kn - 1)
                break;
        }
    }
}
} // namespace Kruskal

class SegTree {
#define lson rt << 1, l, m
#define rson rt << 1 | 1, m + 1, r
public:
    int key[N << 2];
    void build(int a[], int rt, int l, int r)
    {
        if (l == r) {
            key[rt] = a[l];
            return;
        }
        int m = (l + r) >> 1;
        build(a, lson);
        build(a, rson);
        push_up(rt);
    }
    int query(int rt, int l, int r, int L, int R)
    {
        if (L <= l && r <= R) {
            return key[rt];
        }
        int m = (l + r) >> 1;
        int res = 0;
        if (L <= m)
            getmax(res, query(lson, L, R));
        if (m < R)
            getmax(res, query(rson, L, R));
        return res;
    }

private:
    inline void push_up(int rt)
    {
        key[rt] = max(key[rt << 1], key[rt << 1 | 1]);
    }
#undef lson
#undef rson
};
SegTree T;

void dfs(int u, int x)
{
    for (size_t i = 0; i < G[u].size(); i++) {
        int v = G[u][i].first;
        int w = G[u][i].second;
        if (v != x) {
            dep[v] = dep[u] + 1;
            fa[v][0] = u;
            val[v][0] = w;
            dfs(v, u);
        }
    }
}

bool cmpkc(const Que& p, const Que& q)
{
    return p.k < q.k || (p.k == q.k && p.c < q.c);
}

int query(int x, int y)
{
    if (x == 0)
        return 0;
    if (dep[x] > dep[y])
        swap(x, y);
    int res = 0, di = dep[y] - dep[x];
    for (int k = 0; k < FN; k++) {
        if ((di >> k) & 1) {
            getmax(res, val[y][k]);
            y = fa[y][k];
        }
    }
    int k = FN - 1;
    while (x != y) {
        while (k > 0 && fa[x][k] == fa[y][k])
            --k;
        getmax(res, val[x][k]);
        getmax(res, val[y][k]);
        x = fa[x][k];
        y = fa[y][k];
    }
    return res;
}

template <class T>
inline bool read(T& x)
{
    char c;
    int neg = 0;
    if (c = getchar(), c == EOF)
        return false; // EOF
    while (c != '-' && (c < '0' || c > '9'))
        c = getchar();
    if (c == '-')
        neg = 1, c = getchar();
    x = (c - '0');
    while (c = getchar(), c >= '0' && c <= '9')
        x = (x << 3) + (x << 1) + (c - '0');
    if (neg)
        x = -x;
    return true;
}

int main()
{
    int n, m, q;
    read(n);
    read(m);
    read(q);
    {
        using namespace Kruskal;
        for (int i = 1; i <= m; i++) {
            read(eg[i].u);
            read(eg[i].v);
            read(eg[i].w);
        }
        build(n, m);
    } // now G is MST
    fa[1][0] = 1;
    dep[1] = 1;
    dfs(1, 0);
    for (int k = 1; k < FN; k++) {
        for (int i = 1; i <= n; i++) {
            fa[i][k] = fa[fa[i][k - 1]][k - 1];
            val[i][k] = max(val[i][k - 1], val[fa[i][k - 1]][k - 1]);
        }
    }
    for (int i = 1; i <= q; i++) {
        read(que[i].l);
        read(que[i].r);
        read(que[i].k);
        read(que[i].c);
        que[i].x = i;
    }
    sort(que + 1, que + q + 1, cmpkc);
    int tmp[N], tlen;
    for (int x = 1; x <= q; x++) {
        int k = que[x].k, c = que[x].c;
        if (k != que[x - 1].k || c != que[x - 1].c) {
            // not same, rebuild segtree
            tlen = 0;
            for (int i = c; i + k <= n; i += k) {
                tmp[++tlen] = query(i, i + k);
            }
            T.build(tmp, 1, 1, tlen);
        }
        ans[que[x].x] = T.query(1, 1, tlen, (que[x].l - c + k - 1) / k + 1, (que[x].r - c) / k);
    }
    for (int i = 1; i <= q; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}

2015年蓝桥杯省赛C/C++ A组 灾后重建题解(100分),蓝桥杯,A组,灾后重建,题解文章来源地址https://www.toymoban.com/news/detail-730626.html

到了这里,关于2015年蓝桥杯省赛C/C++ A组 灾后重建题解(100分)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 看看去年蓝桥考了什么,第十三届蓝桥杯省赛(C/C++ 大学B组)题解

    本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 九进制正整数 (2022)9 转换成十进制等于多少? 最大运行时间:1s 最大运行内存: 512M 这是一道经典的进制转换题目,具体可以点进链接看看这篇文章。进制转换点击这里!!! 本题为填空题,只

    2024年02月02日
    浏览(50)
  • 第十四届蓝桥杯省赛JavaB组试题E【蜗牛】个人题解Dijkstra堆优化

                                                                                     🍏🍐🍊🍑🍒🍓🫐🥑🍋🍉🥝                                               第十四届蓝桥杯省赛JavaB组试题E【蜗牛】Dijkstra堆

    2023年04月15日
    浏览(40)
  • 第十三届蓝桥杯省赛与国赛真题题解大汇总(十四届参赛者必备)

      大家好,我是执梗。本文汇总了我写的第十三届蓝桥杯所有省赛真题与国赛真题,会针对每道题打出我自己的难度评星⭐️,也会给出每道题的算法标签,帮助大家更有针对性的去学习和准备,当然许多题目由于难度或时间的原因暂未更新,如果有不懂的问题也可以在评

    2024年02月11日
    浏览(75)
  • 2022蓝桥杯省赛——砍竹子

    问题描述 这天, 小明在砍竹子, 他面前有 n 棵竹子排成一排,一开始第 i 棵竹子的 高度为 hi​。 他觉得一棵一棵砍太慢了, 决定使用魔法来砍竹子。魔法可以对连续的一 段相同高度的竹子使用, 假设这一段竹子的高度为 H, 那么用一次魔法可以把这一段竹子的高度都

    2023年04月09日
    浏览(38)
  • 十四届蓝桥杯省赛CB

    写的时候没跑出来,仅仅是因为给 (i*i) 加了括号,爆了int!!! 双精度浮点的表示范围:-1.79E+308 ~ +1.79E+308 基本类型:int 二进制位数:32(4字节) 最小值:Integer.MIN_VALUE= -2147483648 (-2的31次方) 最大值:Integer.MAX_VALUE= 2147483647 (2的31次方-1 double范围很大,基本不可能爆,不

    2024年02月08日
    浏览(42)
  • 2019蓝桥杯省赛题目——“数的分解”

    目录 题目 要求 思路 最后的代码 结果 把 2019 分解成 3 个各不相同的正整数之和,并且要求每个正整数都不包含数字 2 和 4,一共有多少种不同的分解方法? 注意交换 3 个整数的顺序被视为同一种方法,例如 1000+1001+18 和 1001+1000+18 被视为同一种。 这是一道结果填空

    2024年02月14日
    浏览(39)
  • 第十三届蓝桥杯省赛Python 组

    本次所有代码均存放于仓库: Github :GxlHus/Algorithm at 蓝桥杯 (github.com) Gitee :Algorithm: 算法解题 - Gitee.com 原题目:第十三届蓝桥杯大赛软件赛省赛_PB.pdf · AYO/Algorithm - Gitee.com 本次省赛题目总体来说不难,总体质量比较高,尤其是后边几道题,虽然能轻易做出来,但是想跑通所

    2023年04月17日
    浏览(45)
  • 蓝桥杯省赛无忧 STL 课件13 list

    以下是一个示例,展示如何使用listt容器: 在上述示例中,我们首先创建了一个list容器myList,然 后使用push_back()和push_front()函数分别在链表尾部和头 部插入元素。最后,使用范围基于范围的for循环遍历链 表并输出元素。 需要注意的是,由于list是双向链表,因此插入和删除操

    2024年02月02日
    浏览(36)
  • 第九届蓝桥杯省赛——3字母阵列(二维数组)

    仔细寻找,会发现:在下面的8x8的方阵中,隐藏着字母序列:“LANQIAO”。 SLANQIAO ZOEXCCGB MOAYWKHI BCCIPLJQ SLANQIAO RSFWFNYA XIFZVWAL COAIQNAL 我们约定: 序列可以水平,垂直,或者是斜向; 并且走向不限(实际上就是有一共8种方向)。 上图中一共有4个满足要求的串。 下面有一个更大的

    2024年02月15日
    浏览(45)
  • 【十三届蓝桥杯省赛解析javaC组】

    题目描述 解题思路 A题相对比较简单,这题有两种解法 第一种是可以利用记事本把文本复制,然后自己手动排序 第二种是写代码:具体思路是定义一个字符串用来储存问文本,然后把文本转成字符型数组,利用Arrays.sort对字符型数组进行排序,最终实现字符的排序 代码示例

    2023年04月20日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包