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 Kruskalclass 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; // EOFwhile (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 MSTfa[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 segtreetlen = 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;
}

评测记录截图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88023.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

操作系统级 ChatGPT 爆火!

本期推荐开源项目目录&#xff1a; 1. 操作系统贾维斯&#xff1f; 2. 开源翻译模型 3. 整理的 AI 技术资料 01 操作系统贾维斯&#xff1f; 让你的操作系统额变得智能&#xff0c;通过在终端输入自然语言 来让计算机执行一些通用的功能&#xff0c;比如创建、编辑照片、文件夹…

根据excel批量修改文件夹及其文件名称

简介 表哥公司电脑上有一大批文件夹&#xff0c;用于存放一些pdf。他希望对这些文件进行整理。文件夹批量重命名为好记一些的名字&#xff0c;文件夹下的pdf改成的名字格式为&#xff1a;文件夹名序号。 例如&#xff1a;文件夹从“1234”&#xff0c;改成“案件001”&#xf…

亚马逊投资Anthropic; OpenAI将推出新版ChatGPT

&#x1f989; AI新闻 &#x1f680; 亚马逊投资Anthropic获得可靠AI基础模型开发合作 摘要&#xff1a;亚马逊投资Anthropic至多40亿美元&#xff0c;将共同开发可靠高性能的基础模型&#xff0c;并能提前使用Anthropic技术。Anthropic将主要依赖亚马逊的云服务来训练未来的…

如何使用show profile 查看sql的执行周期

修改配置文件/etc/my.cnf 新增一行&#xff1a;query_cache_type1 重启mysql 先开启 show variables like %profiling%; set profiling1;select * from xxx ;show profiles; #显示最近的几次查询show profile cpu,block io for query 编号 #查看程序的执行步骤

微表情识别API + c++并发服务器系统

微表情识别API c并发服务器系统 该项目只开源c并发服务器程序&#xff0c;模型API部分不开源 地址&#xff1a;https://github.com/lin-lai/-API- 更新功能 4.1版本 改用epoll实现IO多路复用并发服务器 项目介绍 本项目用于检测并识别视频中人脸的微表情 目标任务: 用户上…

黑马JVM总结(二十四)

&#xff08;1&#xff09;练习-分析a a:先执行iload1&#xff1a;把数据读入到操作数栈中 iinc&#xff1a;把局部变量表中的1号曹位做一个自增&#xff0c;他在局部变量表中发生的并没有影响到操作数栈 a&#xff1a;限制性自增在做iload 自增变成12 iload把12读取到操作数…

Linux学习之HIS部署(4)

ElasticSearch部署 ElasticSearch资源 RabbitMQ资源 ElasticSearch服务部署 #OpenJDK环境部署 [rootServices ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootServices ~]# yum -y install java-1.8.0-openjdk-devel.x86_64 #安装OpenJDk ... Compl…

微信删除好友对方知道吗?如何加回微信好友?

微信是我们日常生活中使用最多的社交软件&#xff0c;很多小伙伴在使用微信时都曾发出过这样的疑问&#xff1a;微信删除好友对方知道吗&#xff1f;当自己在微信中删除某人后&#xff0c;对方是否会收到信息提醒&#xff1f;另外&#xff0c;如果删除好友后感到后悔&#xff0…

start()方法源码分析

当我们创建好一个线程之后&#xff0c;可以调用.start()方法进行启动&#xff0c;start()方法的内部其实是调用本地的start0()方法&#xff0c; 其实Thread.java这个类中的方法在底层的Thread.c文件中都是一一对应的&#xff0c;在Thread.c中start0方法的底层调用了jvm.cpp文件…

seata的启动与使用

1 下载seata 下载地址&#xff1a;https://github.com/seata/seata/releases/v0.9.0/ 1.1 修改配置文件 将下载得到的压缩包进行解压&#xff0c;进入conf目录&#xff0c;调整下面的配置文件&#xff1a; registry.conf registry {type "nacos"nacos {serverA…

Spring 学习(八)事务管理

1. 事务 1.1 事务的 ACID 原则 数据库事务&#xff08;transaction&#xff09;是访问并可能操作各种数据项的一个数据库操作序列。事务必须满足 ACID 原则——即原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Iso…

uniapp:tabBar点击后设置动画效果

APP端不支持dom操作&#xff0c;也不支持active伪类&#xff0c;绞尽脑汁也没办法给uniapp原生的tabBar点击加动画效果&#xff0c;所以最终只能舍弃原生tabBar&#xff0c;改用自定义tabBar。 自定义tabBar的原理是&#xff0c;页面的上部分分别是tabBar对应的页面组件&#…

Matlab绘图函数subplot、tiledlayout、plot和scatter

一、绘图函数subplot subplot(m,n,p)将当前图窗划分为 mn 网格&#xff0c;并在 p 指定的位置创建坐标区。MATLAB按行号对子图位置进行编号。第一个子图是第一行的第一列&#xff0c;第二个子图是第一行的第二列&#xff0c;依此类推。如果指定的位置已存在坐标区&#xff0c;…

计算物理专题----随机游走实战

计算物理专题----随机游走实战 Problem 1 Implement the 3D random walk 拟合线 自旋的 拟合函数&#xff08;没有数学意义&#xff09; 参数&#xff1a;0.627,3.336,0.603&#xff0c;-3.234 自由程满足在一定范围内的均匀分布以标准自由程为单位长度&#xff0c;…

node的服务端对接科大讯飞-火星ai解决方案

序&#xff1a; 官方给的node对接火星的demo其实只适用于node开发的web应用&#xff0c;但是对于纯node 作为服务端&#xff0c;也就是作为webapi来调用&#xff0c;你会发现&#xff0c;location.host直接是获取不到location的。这个时候&#xff0c;其实要单独起个wss的服务的…

C++: stack 与 queue

目录 1.stack与queue stack queue 2.priority_queue 2.1相关介绍 2.2模拟实现priority_queue --仿函数: --push --pop --top --size --empty --迭代器区间构造 2.3仿函数 3.容器适配器 stack模拟实现 queue模拟实现 学习目标: 1.stack和queue介绍与使用 2.pri…

PHP8中伪变量“$this->”和操作符“::”的使用-PHP8知识详解

对象不仅可以调用自己的变量和方法&#xff0c;也可以调用类中的变量和方法。PHP8通过伪变量“$this->”和操作符“::”来实现这些功能。 1.伪变量“$this->” 在通过对象名->方法调用对象的方法时&#xff0c;如果不知道对象的名称&#xff0c;而又想调用类中的方法…

基于微信小程序的校园代送跑腿系统(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

Mooctest

开发者 测试框架junit 1.字符串不能除 2.a给了c 3. 4. 5.输入是否>0 6.注释

Python中的用法与常见问题解析

装饰器是Python语言中一种强大且常用的概念。通过装饰器&#xff0c;我们可以在不修改原始函数代码的情况下&#xff0c;给函数添加额外的功能&#xff0c;比如日志记录、性能分析、输入验证等。在本文中&#xff0c;我们将深入探讨Python中装饰器的用法和常见问题&#xff0c;…