题意

有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】公路建设的更多相关文章

  1. 4.11 省选模拟赛 序列 二分 线段树优化dp set优化dp 缩点

    容易想到二分. 看到第一个条件容易想到缩点. 第二个条件自然是分段 然后让总和最小 容易想到dp. 缩点为先:我是采用了取了一个前缀最小值数组 二分+并查集缩点 当然也是可以直接采用 其他的奇奇怪怪的 ...

  2. 3.28 省选模拟赛 染色 LCT+线段树

    发现和SDOI2017树点涂色差不多 但是当时这道题模拟赛的时候不会写 赛后也没及时订正 所以这场模拟赛的这道题虽然秒想到了LCT和线段树但是最终还是只是打了暴力. 痛定思痛 还是要把这道题给补了. ...

  3. 【CSA72G】【XSY3316】rectangle 线段树 最小生成树

    题目大意 有一个 \(n\times n\) 的矩阵 \(A\).最开始 \(A\) 中每个元素的值都为 \(0\). 有 \(m\) 次操作,每次给你 \(x_1,x_2,y_1,y_2,w\),对 ...

  4. 【JZOJ5060】【GDOI2017第二轮模拟day1】公路建设 线段树+最小生成树

    题面 在Byteland一共有n 个城市,编号依次为1 到n,它们之间计划修建m条双向道路,其中修建第i 条道路的费用为ci. Byteasar作为Byteland 公路建设项目的总工程师,他决定选定 ...

  5. [bzoj省选十连测推广赛2]T2七彩树

    抄自:http://blog.csdn.net/coldef/article/details/61412577 当时看了就不会,看了别人的题解不懂怎么维护,最后抄了个代码....... 给定一棵n个点 ...

  6. bzoj省选十连测推广赛

    A.普通计算姬 题意:给丁一棵树,每个点有一个权值,用sum(x)表示以x为根的子树的权值和,要求支持两种操作: 1 u v  :修改点u的权值为v. 2 l  r   :  求∑sum[i] l&l ...

  7. 【省选十连测之九】【DP】【组合计数去重】【欧拉函数】基本题

    目录 题意: 输入格式: 输出格式: 数据范围: 思路: 嵌套题的转移 基本题的转移 Part1 Part2 Part3 代码 题意: 这是一个关于括号组合的题. 首先定义一道题是由'(',')',' ...

  8. 【正睿oi省选十连测】第一场

    四小时写了两个暴力??自闭 [原来这就是神仙们的分量Orz rank 56/75 可以说是无比垃圾了 下周目标:进步十名?[大雾 T1 题意:有n个点的图 点有点权Ai 也有点权Bi = A_1 + ...

  9. 洛谷P4065 [JXOI2017]颜色(线段树)

    题意 题目链接 Sol 线段树板子题都做不出来,真是越来越菜了.. 根据题目描述,一个合法区间等价于在区间内的颜色没有在区间外出现过. 所以我们可以对于每个右端点,统计最长的左端点在哪里,刚开始以为这 ...

随机推荐

  1. k-means cluster images

    说明 慕课网上例子,使用k-means算法分类图片, 此处调试运行通过, 并添加包管理内容, 使得其他同学容易运行. 例子地址: https://github.com/fanqingsong/clus ...

  2. unet 网络接受任意大小的输入

    将网络的输入定义的placeholder 大小设为:[None,None,c], 不设置固定的大小. 但是出现问题: 前层特征图与后层特征图进行组合时,尺寸大小不一致: [32, 60, 256] 和 ...

  3. JSP/Serlet 使用fileupload上传文件

    需要引用的jar commons-fileupload-1.3.1.jar commons-io-2.2.jar index.jsp <body> <center> <h ...

  4. Java SE之[静态成员/类成员]与[非静态成员/实例成员]【static】

    定义 静态成员:又称类成员,使用static修饰符的方法和变量: 非静态成员:又称实例成员,未使用static修饰符的方法和变量. 结论 注:jdk1.8 测试源码 public class Main ...

  5. 大受喜欢安卓触控一体机连接云端数据化管理提供例程DEMO

    1.首先,安卓系统坚持了它的开放性,为消费者和开发者同时留出了空间.这是安卓能够快速成长的关键因素.在安卓之前,没有任何一个智能操作系统的开源程度能够像安卓一样.免费开源的安卓系统节约了版权费用. 2 ...

  6. k8s部署etcd数据库集群

    ⒈下载 https://github.com/etcd-io/etcd/releases ⒉解压 tar -zxvf etcd-v3.3.12-linux-amd64.tar.gz ⒊移动可执行文件及 ...

  7. 1、js的基本对象和垃圾回收

    js常用的基本类型:Undefined,null,string,number,boolen 还有一种复杂的数据类型 object.判断类型可以用 typeof. 确定值是否是有穷的,isFinite, ...

  8. spring5.0.2.RELEASE源码环境构建

    Spring5 源码下载注意事项 首先你的JDK 需要升级到1.8 以上.Spring3.0 开始,Spring 源码采用github 托管,不再提供官网下载链接.大家可自行去github 网站下载, ...

  9. thymleaf th:if判断某值不为空

    简单描述:判断后台传递过来的值,是否为空,来做一些业务上的处理. 代码: <div class="col-md-6" th:if="${not #strings.i ...

  10. DISPLAY变量和xhost(原创)

    DISPLAY 在Linux/Unix类操作系统上, DISPLAY用来设置将图形显示到何处. 直接登陆图形界面或者登陆命令行界面后使用startx启动图形, DISPLAY环境变量将自动设置为:0: ...