【省选十连测之一】【线段树】【最小生成树之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 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...
随机推荐
- node服务端口被占用Error listen EADDRINUSE :::3000
Error: listen EADDRINUSE: address already in use :::3000,出现这个报错说明3000端口被占用 解决方法:找到占用该端口的程序,kill杀掉它就可 ...
- 深入浅出mybatis之缓存机制
目录 前言 准备工作 MyBatis默认缓存设置 缓存实现原理分析 参数localCacheScope控制的缓存策略 参数cacheEnabled控制的缓存策略 总结 前言 提到缓存,我们都会不约而同 ...
- 暑假闲着没事第一弹:基于Django的长江大学教务处成绩查询系统
本篇文章涉及到的知识点有:Python爬虫,MySQL数据库,html/css/js基础,selenium和phantomjs基础,MVC设计模式,ORM(对象关系映射)框架,django框架(Pyt ...
- 洛谷红名+AC150祭
emmmm没什么想说的,随便放个图吧23333(逃~
- webpack-dev-server live reloading 技术实现
webpack-dev-server live reloading https://github.com/webpack/webpack-dev-server Use webpack with a ...
- 作业二Wordcount
1,github地址 https://github.com/dtneverdie/word-count 2,PSP表格 3,解题思路 先从理论上判断应该先将文件内的字符全部读入,然后根据分隔符来进行单 ...
- 分布式系列十五: MongoDB数据库
MongoDB 是基于分布式文件存储的数据库. 开发语言是C++. 具有高性能,可扩展的特点. 是NoSql中最像关系数据库的. 什么是NoSql NoSQL 是 Not only SQL 的缩写. ...
- Spring框架中的org.springframework.context.annotation.Import注解类
@Import注解的作用和在使用spring的xml配置时用到的<import/>类似.但应注意是@Import在使用时必须要保证能被IOC容器扫描到,所以通常它会和@Configurat ...
- 【原创】大数据基础之Impala(3)部分调优
1)将coordinator和executor角色分离 By default, each host in the cluster that runs the impalad daemon can ac ...
- windows 下 配置 github
github 功能介绍 1. 记录多个版本 2.查看历史操作,可以进行版本回退和前进的控制 3. 多端共享代码,自动合成 Github 与 SVN 1. SVN 版本集中管理,所 ...