【省选十连测之一】【线段树】【最小生成树之Kruskal】公路建设
题意
有n个点,m条双向道路,其中第条公路的两个端点是u[i],v[i],费用是c[i]。
现在给出q个询问,每次给定一个L和一个R,要求你只能够使用[L,R]这个区间内的边,是的连接之后,连通块的数量最小。在保证连通块数量最小的情况下,求最少需要的代价(可以拿一些边不用)。
输入格式
第一行三个整数n,m,q,含义如图所示
接下来m行,每行3个整数,描述一条边,分别是u,v,c。
接下来q行,每行2个整数L,R,表示一次询问。
输出格式
对于每一个询问,输出一个整数表示最小连通块意义下的最小代价。
数据范围
n<=100,m<100000,q<=15000
思路
这道题还正是神奇...
看完题目之后,不难得出题目的要求就是让我们用[L,R]之间的边求一个最小生成树。然后我们就有了一个\(O(m*q*\log m )\)的暴力算法。
然而在考场上,自己脑残了...想了几分钟之后,这不是...回滚莫队吗?正好可以去掉难搞的删除操作,然后...再用LCT来回答LCA...好像不可做欸...然后全程想怎么优化...然后就...GG。
看完题解之后,发现好像以前见过类似的做法,但是...似乎有没有...
题解的大致思路就是:
对于m条边建一个线段树,下标就是边的标号,然后对于一个线段树区间[L,R],这个节点上存的是使用[L,R]这个区间的边,求得的最小生成树使用了的边的编号(用数组存在线段树结点里)。因为最多会使用n-1条边,那么就最多会存\(O(m * 4 *n)\)个数字,是不会炸的。
然后怎么合并呢?因为我们一个节点里最多只会塞n-1条边(体现为编号),那么我们就可以直接暴力合并!也就是用Kruskal暴力合并即可。但需要注意的是,不能直接把当前线段树节点的左右儿子的边集直接存下来然后排序跑Kruskal,而应该利用归并排序的思想,直接用双指针在左右两个儿子的边集中扫一下就可以了。
然后处理询问的时候就利用线段树的结构,将询问的区间拆成\(\log m\)个子区间,然后暴力合并即可。
最后总的时间复杂度就是\(O((m\log m+q\log m)n*f(n))\),其中f(n)代表的是n个点的路径压缩版本的并查集的复杂度。
去你的回滚莫队
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 100
#define MAXM 100000
#define MAXQ 15000
using namespace std;
struct edge
{
int u,v,w;
edge(){};
edge(int _u,int _v,int _w):u(_u),v(_v),w(_w){};
}edges[MAXM+5];
bool operator < (const edge &a,const edge &b)
{return a.w<b.w;}
bool cmp(const int &a,const int &b)
{return edges[a]<edges[b];}
struct TreeNode
{
int seq[MAXN*2+5];//第0位存长度
}tree[MAXM*4];
int n,m,q,setfa[MAXN+5];
int ANS[MAXN*2+5],ANSval;//答案用的边集ANS[],以及最小代价ANSval
int Findfa(int x)
{
if(setfa[x]==x)
return x;
return setfa[x]=Findfa(setfa[x]);
}
bool Union(int x,int y)
{
x=Findfa(x),y=Findfa(y);
if(x==y)
return false;
setfa[x]=y;
return true;
}
void Init()
{
for(int i=1;i<=n;i++)
setfa[i]=i;
}
void Merge(int *seq,int *seq1,int *seq2)//把seq1和seq2合并到seq中
{
static int tmp[MAXN*2+5];//归并排序进行Kruskal,以规避sort的log(卡常数)
tmp[0]=0;Init();
int i=1,j=1;
while(i<=seq1[0]||j<=seq2[0])
{
if(j>seq2[0]||(i<=seq1[0]&&edges[seq1[i]].w<edges[seq2[j]].w))
{
if(Union(edges[seq1[i]].u,edges[seq1[i]].v))
tmp[++tmp[0]]=seq1[i];
i++;
}
else
{
if(Union(edges[seq2[j]].u,edges[seq2[j]].v))
tmp[++tmp[0]]=seq2[j];
j++;
}
}
for(int i=0;i<=tmp[0];i++)
seq[i]=tmp[i];
}
void Build(int i,int l,int r)
{
if(l==r)
{
tree[i].seq[0]=1;
tree[i].seq[1]=l;
return;
}
int mid=(l+r)/2;
Build(i*2,l,mid);
Build(i*2+1,mid+1,r);
Merge(tree[i].seq,tree[i*2].seq,tree[i*2+1].seq);
}
void Query(int i,int l,int r,int ql,int qr)
{
if(qr<l||ql>r)
return;
if(ql<=l&&r<=qr)
{
Merge(ANS,ANS,tree[i].seq);
return;
}
int mid=(l+r)/2;
Query(i*2,l,mid,ql,qr);
Query(i*2+1,mid+1,r,ql,qr);
}
inline int read()
{
int ret=0;char c=0;
while(c<'0'||c>'9') c=getchar();
ret=10*ret+c-'0';
while(true){c=getchar();if(c<'0'||c>'9') break;ret=10*ret+c-'0';}
return ret;
}
inline void print(int x)
{
if(x==0) return;
print(x/10);
putchar(x%10+'0');
}
int main()
{
// freopen("highway.in","r",stdin);
// freopen("highway.out","w",stdout);
scanf("%d %d %d",&n,&m,&q);
int u,v,c;
for(int i=1;i<=m;i++)
{
u=read(),v=read(),c=read();
edges[i]=edge(u,v,c);
}
Build(1,1,m);
for(int i=1;i<=q;i++)
{
int l,r;
l=read(),r=read();
ANS[0]=0,ANSval=0;
Query(1,1,m,l,r);
for(int i=1;i<=ANS[0];i++)
ANSval+=edges[ANS[i]].w;//最后统一算答案
print(ANSval);putchar('\n');
}
return 0;
}
/*
3 5 2
1 3 2
2 3 1
2 1 6
3 1 7
2 3 7
2 5
3 4
*/
【省选十连测之一】【线段树】【最小生成树之Kruskal】公路建设的更多相关文章
- 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点
容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...
- 3.28 省选模拟赛 染色 LCT+线段树
发现和SDOI2017树点涂色差不多 但是当时这道题模拟赛的时候不会写 赛后也没及时订正 所以这场模拟赛的这道题虽然秒想到了LCT和线段树但是最终还是只是打了暴力. 痛定思痛 还是要把这道题给补了. ...
- 【CSA72G】【XSY3316】rectangle 线段树 最小生成树
题目大意 有一个 \(n\times n\) 的矩阵 \(A\).最开始 \(A\) 中每个元素的值都为 \(0\). 有 \(m\) 次操作,每次给你 \(x_1,x_2,y_1,y_2,w\),对 ...
- 【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树
题面 在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci. Byteasar作为Byteland 公路建设项目的总工程师,他决定选定 ...
- [bzoj省选十连测推广赛2]T2七彩树
抄自:http://blog.csdn.net/coldef/article/details/61412577 当时看了就不会,看了别人的题解不懂怎么维护,最后抄了个代码....... 给定一棵n个点 ...
- bzoj省选十连测推广赛
A.普通计算姬 题意:给丁一棵树,每个点有一个权值,用sum(x)表示以x为根的子树的权值和,要求支持两种操作: 1 u v :修改点u的权值为v. 2 l r : 求∑sum[i] l&l ...
- 【省选十连测之九】【DP】【组合计数去重】【欧拉函数】基本题
目录 题意: 输入格式: 输出格式: 数据范围: 思路: 嵌套题的转移 基本题的转移 Part1 Part2 Part3 代码 题意: 这是一个关于括号组合的题. 首先定义一道题是由'(',')',' ...
- 【正睿oi省选十连测】第一场
四小时写了两个暴力??自闭 [原来这就是神仙们的分量Orz rank 56/75 可以说是无比垃圾了 下周目标:进步十名?[大雾 T1 题意:有n个点的图 点有点权Ai 也有点权Bi = A_1 + ...
- 洛谷P4065 [JXOI2017]颜色(线段树)
题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...
随机推荐
- System系统类
System系统类 : 主要的作用是用于获取系统的一个参数. System类需要掌握的方法: arraycopy(Object src, int srcPos, Object dest, int de ...
- [Reinforcement Learning] 强化学习介绍
随着AlphaGo和AlphaZero的出现,强化学习相关算法在这几年引起了学术界和工业界的重视.最近也翻了很多强化学习的资料,有时间了还是得自己动脑筋整理一下. 强化学习定义 先借用维基百科上对强化 ...
- Python面向对象之反射
一.反射的基本概念 二.反射示例 三.反射的应用 一.反射的基本概念 反射:可以用字符串的方式去访问对象的属性,调用对象的方法(但是不能去访问方法),Python中一切皆对象,都可以使用反射. 反射有 ...
- UE4命令行使用,解释
命令行在外部 从命令行运行编辑项目 1 导航到您的[LauncherInstall][VersionNumber]\Engine\Binaries\Win64 目录中. 2 右键单击上 UE4Edit ...
- 【模板】最长公共子序列(LCS)。
看过好多人的博客,感觉要么是太复杂要么就是太不容易理解. 那就亲自动手写一个通俗易懂的. 先定义两个数组,第一个数组为主,用第二个数组来匹配第一个,看能有多少可以对应上的. 所以,其实第一个数组的内容 ...
- C++变量/函数命名规范
## 参照Google C++编程规范之变量命名 1. 变量 变量名一律小写,单词间以下划线相连.类的成员变量以下划线结尾. 普通变量命名 举例: string window_name; // OK ...
- springboo+nginx测试负载均衡
1:之前只是用nginx调用了boot_8044这一个服务,这次新建一个boot_8055服务,并在linux上启动: 两个boot我都是放在 /myprojects 目录下的(自定义,能启动就行) ...
- IIS 运行ASP.Net的基本配置(编辑中。。。)
今天在新建的IIS上运行Asp.net 程序,发现IIS根本没有走asp的路由系统,直接返回了404,后来发现是IIS没有正确安装,需要安装以下的组件: 未安装前,IIS里的样子: 安装后,IIS的样 ...
- django-form字段form 和插件widgets
26)django-form字段和插件widgets 创建Form类时,主要涉及到 [字段] 和 [插件],字段用于对用户请求数据的验证,插件用于自动生成HTML 一:常用字段 1.Django中Fo ...
- Python———pandas数据处理
pandas模块 更高级的数据分析工具基于NumPy构建包含Series和DataFrame两种数据结构,以及相应方法 调用方法:from pandas import Series, DataFra ...