题目

Pear市一共有N(<=50000)个居民点,居民点之间有M(<=200000)条双向道路相连。这些居民点两两之间都可以通过双向道路到达。
这种情况一直持续到最近,一次严重的地震毁坏了全部M条道路。
震后,Pear打算修复其中一些道路,修理第i条道路需要Pi的时间。不过,Pear并不打算让全部的点连通,而是选择一些标号特殊的点让他们连通。
Pear有Q(<=50000)次询问,每次询问,他会选择所有编号在[tl,tr]之间,并且 编号 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 , 不能通过工程设置而省略常用头文件。

提交时,注意选择所期望的编译器类型。

代码

easy

#include <iostream>
#include <set>using namespace std;
int N, M, Q;
const int MaxM = 2e5;//修正4:不能用10^5
const int MaxN = 50001;/*边的抽象*/
struct Edge {int from, to, cost;//起点,终点,代价Edge(int from, int to, int cost) {this->from = from;this->to = to;this->cost = cost;}
};bool cmp(Edge *e1, Edge *e2) {return e1->cost < e2->cost;
}Edge *edges[MaxM];/*并查集*/
struct UFNode {UFNode *parent;UFNode() : parent(NULL) {}
};UFNode *find(UFNode *p) {if (p->parent == NULL)return p;set<UFNode *> path;while (p->parent != NULL) {path.insert(p);p = p->parent;}
//路径压缩,让每个节点都能直接到达集团老大set<UFNode *>::iterator iter = path.begin();while (iter != path.end()) {(*iter)->parent = p;iter++;//修正1.指针后移}return p;
}void merge(UFNode *p1, UFNode *p2) {find(p2)->parent = find(p1);
}UFNode ufnodes[MaxN];//并查集的节点,一开始各自独立void easy(int l, int r, int mod, int c) {for (int j = 0; j <=N ; ++j) {ufnodes[j].parent=NULL;//修正2:重新初始化}//    逐步加入边到最小生成树中for (int i = 0; i < M; ++i) {Edge *pEdge = edges[i];int from = pEdge->from;int to = pEdge->to;int cost = pEdge->cost;if (find(&ufnodes[from]) == find(&ufnodes[to]))//这两个点已经在一棵树上,这条边不能采纳continue;elsemerge(&ufnodes[from], &ufnodes[to]);//      如果这里求最小生成树,if cnt==N-1 最小树已经生成UFNode *parent = NULL;bool isOk=true;for (int i = l; i <= r; ++i) {if (i % mod == c)//i是关注点的编号{if (parent == NULL)parent = find(&ufnodes[i]);//第一个关注点的老大elseif(parent!=find(&ufnodes[i]))//没有联通{isOk=false;break;}}}if(isOk)//关注点都联通了{printf("%d\n",cost);break;}}
}int main(int argc, const char *argv[]) {//    freopen("/Users/zhengwei/CLionProjects/lanqiao2018/2015_c_a/in/in5.txt", "r", stdin);scanf("%d %d %d", &N, &M, &Q);for (int i = 0; i < M; ++i) {int a, b, c;scanf("%d %d %d", &a, &b, &c);Edge *e = new Edge(a, b, c);edges[i] = e;}
//    排序sort(edges, edges + M, cmp);//修正3.排序边界for (int i = 0; i < Q; ++i) {int l, r, mod, c;scanf("%d %d %d %d", &l, &r, &mod, &c);easy(l, r, mod, c);}return 0;
}

mid

#include <iostream>
#include <algorithm>
#include <vector>
#include <set>using namespace std;
int N, M, Q;
const int MaxM = 2e5;//修正4:不能用10^5
const int MaxN = 50001;/*边的抽象*/
struct Edge {int from, to, cost;//起点,终点,代价Edge(int from, int to, int cost) {this->from = from;this->to = to;this->cost = cost;}
};bool cmp(Edge *e1, Edge *e2) {return e1->cost < e2->cost;
}Edge *edges[MaxM];/*并查集*/
struct UFNode {UFNode *parent;UFNode() : parent(NULL) {}
};UFNode *find(UFNode *p) {if (p->parent == NULL)return p;set<UFNode *> path;while (p->parent != NULL) {path.insert(p);p = p->parent;}set<UFNode *>::iterator iter = path.begin();while (iter != path.end()) {(*iter)->parent = p;iter++;//修正1.指针后移}return p;
}void merge(UFNode *p1, UFNode *p2) {find(p2)->parent = find(p1);
}UFNode ufnodes[MaxN];//并查集的节点,一开始各自独立/*最小树的生成及表示*/
vector<Edge *> mst[MaxN];void buildMST() {int cnt = 0;//已加入边的数量for (int i = 0; i < M; ++i) {Edge *pEdge = edges[i];int from = pEdge->from;int to = pEdge->to;int cost = pEdge->cost;if (find(&ufnodes[from]) == find(&ufnodes[to]))//这两个点已经在一棵树上,这条边不能采纳continue;else {merge(&ufnodes[from], &ufnodes[to]);cnt++;
//            将边加入到mst(邻接表)mst[from].push_back(pEdge);Edge *other = new Edge(to, from, cost);mst[to].push_back(other);if (cnt == N - 1)//构建完成{break;}}}
}int ff[MaxN][17];//ff[i][j]指的是标号为i的节点往根节点的方向移动2^i次达到的节点的标号  ff[i][j]=ff[ff[i][j-1]][j-1]
int mm[MaxN][17];//mm[i][j]指的是标号为i的节点往根节点的方向移动2^i次过程中的最大权
int depth[MaxN];//记录每个点在mst中的深度
int vis[MaxN];//记录某个点是否被访问过
/**** @param start 开始的点标号* @param parent 父节点标号* @param depth 这个点的深度*/
void dfs(int start, int parent, int d) {depth[start] = d + 1;vis[start] = 1;
//    先向上走for (int i = 1; i < 17; ++i) {ff[start][i] = ff[ff[start][i - 1]][i - 1];mm[start][i] = max(mm[start][i - 1], mm[ff[start][i - 1]][i - 1]);}
//    向下递归,找到所有儿子(所有邻居)for (int i = 0; i < mst[start].size(); ++i) {Edge *child = mst[start][i];//儿子if (vis[child->to])continue;ff[child->to][0] = start;mm[child->to][0] = child->cost;dfs(child->to, start, d + 1);}
}void preLca() {ff[1][0] = 1;//定义1号节点为根节点mm[1][0] = 0;//定义1号节点为根节点,它向上一步就没了,dfs(1, 1, 0);
}
/*倍增法,求lca,顺便求max权重*/
int maxUsingLca(int a, int b) {int ans = -1;
//    1.将a深度调到更深(交换)if (depth[a] < depth[b]) {int t = a;a = b;b = t;}
//2.将a调到和b同一高度int k = depth[a] - depth[b];//高度差for (int i = 0; (1 << i) <= k; ++i) {//k的二进制101if ((1 << i) & k)//k二进制的第i(从右往左)位是1{ans = max(ans, mm[a][i]);a = ff[a][i];}}
//    至此,a和b已经在同一层上
//从最顶层开始遍历,求ab两点的lca的下一层if(a!=b) {//重要更新for (int j = 16; j >= 0; --j) {if (ff[a][j] == ff[b][j])continue;//从最大祖先开始,判断a,b祖先,是否相同,// 一开始肯定相同,直到它们都跳j到最近祖先的下一层时,这个else触发else {ans = max(ans, mm[a][j]);ans = max(ans, mm[b][j]);a = ff[a][j];b = ff[b][j];
//            break;//重要更新}}
//    至此,a,b离lca还差一步
//再往上走一步就得到了lcaans = max(ans, mm[a][0]);ans = max(ans, mm[b][0]);}return ans;
}
void mid(int l, int r, int mod, int c) {int ans = -1;int left = l - l % mod + c;if (left < l)left += mod;
//    遍历关注点,两两在mst中用倍增法求lca顺便求max权重for (; left + mod <= r; left += mod) {ans = max(ans, maxUsingLca(left, left + mod));}printf("%d\n", ans);
}int main(int argc, const char *argv[]) {//    freopen("/Users/zhengwei/CLionProjects/lanqiao2018/2015_c_a/in/in5.txt", "r", stdin);scanf("%d %d %d", &N, &M, &Q);for (int i = 0; i < M; ++i) {int a, b, c;scanf("%d %d %d", &a, &b, &c);Edge *e = new Edge(a, b, c);edges[i] = e;}
//    排序sort(edges, edges + M, cmp);//修正3.排序边界buildMST();//生成最小树preLca();//在最小树为倍增法做预处理for (int i = 0; i < Q; ++i) {int l, r, mod, c;scanf("%d %d %d %d", &l, &r, &mod, &c);mid(l, r, mod, c);}return 0;
}

hard

#include <iostream>
#include <vector>
#include <set>using namespace std;
int N, M, Q;
const int MaxM = 2e5;//修正4:不能用10^5
const int MaxN = 50001;/*边的抽象*/
struct Edge {int from, to, cost;//起点,终点,代价Edge(int from, int to, int cost) {this->from = from;this->to = to;this->cost = cost;}
};bool cmp(Edge *e1, Edge *e2) {return e1->cost < e2->cost;
}Edge *edges[MaxM];/*并查集*/
struct UFNode {UFNode *parent;UFNode() : parent(NULL) {}
};UFNode *find(UFNode *p) {if (p->parent == NULL)return p;set<UFNode *> path;while (p->parent != NULL) {path.insert(p);p = p->parent;}set<UFNode *>::iterator iter = path.begin();while (iter != path.end()) {(*iter)->parent = p;iter++;//修正1.指针后移}return p;
}void merge(UFNode *p1, UFNode *p2) {find(p2)->parent = find(p1);
}UFNode ufnodes[MaxN];//并查集的节点,一开始各自独立/*最小树的生成及表示*/
vector<Edge *> mst[MaxN];void buildMST() {int cnt = 0;//已加入边的数量for (int i = 0; i < M; ++i) {Edge *pEdge = edges[i];int from = pEdge->from;int to = pEdge->to;int cost = pEdge->cost;if (find(&ufnodes[from]) == find(&ufnodes[to]))//这两个点已经在一棵树上,这条边不能采纳continue;else {merge(&ufnodes[from], &ufnodes[to]);cnt++;
//            将边加入到mst(邻接表)mst[from].push_back(pEdge);Edge *other = new Edge(to, from, cost);mst[to].push_back(other);if (cnt == N - 1)//构建完成{break;}}}
}/*lca及最值查询*/
int ff[MaxN][17];//ff[i][j]指的是标号为i的节点往根节点的方向移动2^i次达到的节点的标号  ff[i][j]=ff[ff[i][j-1]][j-1]
int mm[MaxN][17];//mm[i][j]指的是标号为i的节点往根节点的方向移动2^i次过程中的最大权
int depth[MaxN];//记录每个点在mst中的深度
int vis[MaxN];//记录某个点是否被访问过
/**** @param start 开始的点标号* @param parent 父节点标号* @param depth 这个点的深度*/
void dfs(int start, int parent, int d) {depth[start] = d + 1;vis[start] = 1;
//    先向上走for (int i = 1; i < 17; ++i) {ff[start][i] = ff[ff[start][i - 1]][i - 1];mm[start][i] = max(mm[start][i - 1], mm[ff[start][i - 1]][i - 1]);}
//    向下递归,找到所有儿子(所有邻居)for (int i = 0; i < mst[start].size(); ++i) {Edge *child = mst[start][i];//儿子if (vis[child->to])continue;ff[child->to][0] = start;mm[child->to][0] = child->cost;dfs(child->to, start, d + 1);}
}void preLca() {ff[1][0] = 1;//定义1号节点为根节点mm[1][0] = 0;//定义1号节点为根节点,它向上一步就没了,dfs(1, 1, 0);
}/*倍增法,求lca,顺便求max权重*/
int maxUsingLca(int a, int b) {int ans = -1;
//    1.将a深度调到更深(交换)if (depth[a] < depth[b]) {int t = a;a = b;b = t;}
//2.将a调到和b同一高度int k = depth[a] - depth[b];//高度差for (int i = 0; (1 << i) <= k; ++i) {//k的二进制101if ((1 << i) & k)//k二进制的第i(从右往左)位是1{ans = max(ans, mm[a][i]);a = ff[a][i];}}
//    至此,a和b已经在同一层上
//从最顶层开始遍历,求ab两点的lca的下一层if (a != b) {//=========此处为重要更新=========for (int j = 16; j >= 0; --j) {if (ff[a][j] == ff[b][j])continue;//从最大祖先开始,判断a,b祖先,是否相同,// 一开始肯定相同,直到它们都跳j到最近祖先的下一层时,这个else触发else {ans = max(ans, mm[a][j]);ans = max(ans, mm[b][j]);a = ff[a][j];b = ff[b][j];
//            break;//重要更新,此处不能break}}
//    至此,a,b离lca还差一步
//再往上走一步就得到了lcaans = max(ans, mm[a][0]);ans = max(ans, mm[b][0]);}return ans;
}void mid(int l, int r, int mod, int c) {int ans = -1;int left = l - l % mod + c;if (left < l)left += mod;
//    遍历关注点,两两在mst中用倍增法求lca顺便求max权重for (; left + mod <= r; left += mod) {int l = maxUsingLca(left, left + mod);ans = max(ans, l);}printf("%d\n", ans);
}/*线段树的定义,构建,及查询*/
struct SegTree {int l, r, maxX;SegTree *lson, *rson;
};int data[MaxN];//用来存储线段树的原始数据
SegTree *buildSegTree(int l, int r) {SegTree *stree = new SegTree();stree->l = l;stree->r = r;if (l == r) {stree->maxX = data[l];return stree;}int mid = (l + r) / 2;stree->lson = buildSegTree(l, mid);stree->rson = buildSegTree(mid + 1, r);stree->maxX = max(stree->lson->maxX, stree->rson->maxX);return stree;
}int queryInSegTree(SegTree *root, int p1, int p2) {int l = root->l;int r = root->r;if (p1 <= l && p2 >= r)return root->maxX;//p1,p2完全覆盖l~r的时候直接返回int mid = (l + r) / 2;int ans = -1;if (p1 <= mid)ans = max(ans, queryInSegTree(root->lson, p1, p2));if (p2 > mid)ans = max(ans, queryInSegTree(root->rson, p1, p2));return ans;
}void hard(int l, int r, int mod, int c, SegTree *segTrees[]) {SegTree *tree = segTrees[mod * (mod - 1) / 2 + c + 1];int p1 = 0;if (l <= c)p1 = 1;elsep1 = (l - c) % mod == 0 ? (l - c) / mod + 1 : (l - c) / mod + 2;int p2 = (r - c) / mod;int ans = queryInSegTree(tree, p1, p2);printf("%d\n", ans);
}int main(int argc, const char *argv[]) {freopen("/Users/zhengwei/CLionProjects/lanqiaobei2019/2015_A/data10/in8.txt", "r", stdin);freopen("/Users/zhengwei/CLionProjects/lanqiaobei2019/2015_A/data10/myout8.txt", "w", stdout);scanf("%d %d %d", &N, &M, &Q);for (int i = 0; i < M; ++i) {int a, b, c;scanf("%d %d %d", &a, &b, &c);Edge *e = new Edge(a, b, c);edges[i] = e;}
//    排序sort(edges, edges + M, cmp);//修正3.排序边界buildMST();//生成最小树preLca();//在最小树为倍增法做预处理int threshold = min(70, N / 3);/*生成很多的线段树,具体来说,对小于等于70的每个mod,每个c都生成一颗线段树*/SegTree *segTrees[threshold * (threshold + 1) / 2 + 1];int index = 1;
//    对每个modfor (int _mod = 1; _mod <= threshold; ++_mod) {//        对每个余数
/*        {//针对re=0,余数为0的情况int k = 1;
//            迭代1~N中符合条件的关注点,两两连通求最大权重,存储在data中for (; (k + 1) * _mod < N; k++) {data[k] = maxUsingLca(k * _mod, (k + 1) * _mod);}segTrees[index++] = buildSegTree(1, k);}*/for (int re = 0; re < _mod; ++re) {//具体来说1~N之间有多个关注点满足%mod=c的情况,把这些点两两第计算出max权重,存储在区间树的原始数据中
//并依次来生成区间树int k = 0;
//            迭代1~N中符合条件的关注点,两两连通求最大权重,存储在data中for (; (k + 1) * _mod + re <= N; k++) {data[k + 1] = maxUsingLca(k * _mod + re, (k + 1) * _mod + re);}segTrees[index++] = buildSegTree(1, k);}}for (int i = 0; i < Q; ++i) {int l, r, mod, c;scanf("%d %d %d %d", &l, &r, &mod, &c);if (mod > threshold)mid(l, r, mod, c);elsehard(l, r, mod, c, segTrees);}return 0;
}

征战蓝桥 —— 2015年第六届 —— C/C++A组第10题——灾后重建相关推荐

  1. 征战蓝桥 —— 2015年第六届 —— C/C++A组第5题——九数组分数

    九数组分数 1,2,3-9 这九个数字组成一个分数,其值恰好为1/3,如何组法? 下面的程序实现了该功能,请填写划线部分缺失的代码. #include <stdio.h>void test ...

  2. 征战蓝桥 —— 2015年第六届 —— C/C++A组第4题——格子中输出

    格子中输出 StringInGrid函数会在一个指定大小的格子中打印指定的字符串. 要求字符串在水平.垂直两个方向上都居中. 如果字符串太长,就截断. 如果不能恰好居中,可以稍稍偏左或者偏上一点. 下 ...

  3. 征战蓝桥 —— 2016年第七届 —— C/C++A组第10题——最大比例

    题目 X星球的某个大奖赛设了M级奖励.每个级别的奖金是一个正整数. 并且,相邻的两个级别间的比例是个固定值. 也就是说:所有级别的奖金数构成了一个等比数列.比如: 16,24,36,54 其等比值为: ...

  4. 征战蓝桥 —— 2016年第七届 —— C/C++A组第8题——四平方和

    题目 四平方和定理,又称为拉格朗日定理: 每个正整数都可以表示为至多4个正整数的平方和. 如果把0包括进去,就正好可以表示为4个数的平方和. 比如: 5 = 0^2 + 0^2 + 1^2 + 2^2 ...

  5. 征战蓝桥 —— 2016年第七届 —— C/C++A组第3题——方格填数

    题目 如下的10个格子 (如果显示有问题,也可以参看[图7-1.jpg]) 填入0~9的数字.要求:连续的两个数字不能相邻. (左右.上下.对角都算相邻) 一共有多少种可能的填数方案? 请填写表示方案 ...

  6. 征战蓝桥 —— 2016年第七届 —— C/C++A组第7题——剪邮票

    剪邮票 如[图1.jpg], 有12张连在一起的12生肖的邮票. 现在你要从中剪下5张来,要求必须是连着的. (仅仅连接一个角不算相连) 比如,[图2.jpg],[图3.jpg]中,粉红色所示部分就是 ...

  7. 征战蓝桥 —— 2016年第七届 —— C/C++A组第5题——消除尾一

    题目 下面的代码把一个整数的二进制表示的最右边的连续的1全部变成0 如果最后一位是0,则原数字保持不变. 如果采用代码中的测试数据,应该输出: 0000000000000000000000000110 ...

  8. 征战蓝桥 —— 2016年第七届 —— C/C++A组第2题——生日蜡烛

    题目 某君从某年开始每年都举办一次生日party,并且每次都要吹熄与年龄相同根数的蜡烛. 现在算起来,他一共吹熄了236根蜡烛. 请问,他从多少岁开始过生日party的? 请填写他开始过生日party ...

  9. 征战蓝桥 —— 2016年第七届 —— C/C++A组第4题——快速排序

    题目 排序在各种场合经常被用到. 快速排序是十分常用的高效率的算法. 其思想是:先选一个"标尺", 用它把整个队列过一遍筛子, 以保证:其左边的元素都不大于它,其右边的元素都不小于 ...

最新文章

  1. 我有一个计划001之数据挖掘面试(更新ing)
  2. Android开发之异步任务加载网络图片并存储在sdcard中(源代码分享)
  3. 电中在线计算机应用基础二考试题目及答案,最新电大2015计算机应用基础作业2 答案.doc...
  4. 【硬核干货】2500字全方面解读Python的格式化输出
  5. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown database 'test'
  6. mysql绿色版安装
  7. 服务器可否替代手机芯片,基于ARM的处理器能取代桌面处理器吗?
  8. Android开发中无处不在的设计模式——动态代理模式
  9. 【clickhouse】如何处理ClickHouse超时问题
  10. php 微信获取code,微信网页授权接口为什么获取不到code(已解决)
  11. php 什么是函数式编程,函数式编程的介绍和归纳总结(附代码)
  12. 串行通信(二):串口流控
  13. 阶段3 2.Spring_06.Spring的新注解_6 Qualifier注解的另一种用法
  14. windows服务器系统和专业版差别,Win10专业版和企业版哪个好?教你区分win10企业版和专业版...
  15. 假设检验基本思想与步骤
  16. ai怎么做盒子效果图_AI制作包装纸盒贴图教程
  17. flask sqlalchemy按照创建时间逆向排序
  18. 翻译文章-让生活变得简单
  19. C#判断用户是否使用微信浏览器,并据此来显示真实内容或二维码
  20. 数据驾驶舱只是面子工程?它的真正作用你根本就不了解

热门文章

  1. ASP.NET MVC经典项目ProDinner项目解析(3)
  2. 海思芯片硬件java加速_海思Hi3719C V100芯片简介
  3. vue前端服务器端口_解密智联招聘的大前端架构 Ada
  4. C语言简单题-找最大的字符串
  5. 驱动备份工具哪个好_原神元素反应工具人推荐一览 元素反应工具人哪个好
  6. C语言学习之有4个圆塔,圆心分别为(2,2)、(-2,2)、(-2,-2)、(2,-2),圆半径为1
  7. boost log 能不能循环覆盖_记一次for循环中let是声明还是赋值
  8. VS2008 Tips #008 如何创建ASP.NET Web 用户控件并包含在Web 页面中
  9. 简述systemd的新特性及unit常见类型分析、使用systemd管理编译安装的nginx
  10. 上传文件(Uploading Files)