【bzoj2238】Mst(树链剖分+线段树)
2238: Mst
Time Limit: 20 Sec Memory Limit: 256 MB
Submit: 465 Solved: 131
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 2 3
1 3 5
2 3 9
2 4 1
4
1
2
3
4
Sample Output
13
9
Not connected
数据规模
另外30%的数据,$N<=1000$。
100%的数据如题目。




这时结论就出来了:一条非树边可以代替其两端点在树上的简单路径之间的所有边
证明:

以这个图为例,一条非树边与其两端点在树上的简单路径组成的是一个x个点x条边的环,而这x个点可以删去其中一条边,只用x-1条边连通,因此这条非树边可以代替简单路径中的任意一条树边。
相反,这条非树边与 不在上述简单路径中的树边 只能形成x个点x-1条边的树,删去任意一条边都会使这棵树不连通,而这棵树作为原最小生成树的一棵子树,这棵子树不连通的话原最小生成树一定也不连通。所以这条非树边不能代替任意一条非简单路径上的边。
如何快速处理树上的简单路径?树链剖分!
所以算法明确了:先预处理出最小生成树,然后将这棵最小生成树进行树链剖分,每一条边记录能够代替它的、权值最小的非树边的长度。对于每条非树边,利用树剖快速更新其两端点在树上的简单路径之间的所有边记录的最小值。查询时直接查询删除的边上存储的最小值即可。
实际代码可以将边的信息存在其儿子节点上,简化代码复杂度
有两种情况要特判Not connected:
原图不连通,对于所有询问都输出Not connected(树链剖分只是维护用其它边替换一条边的情况,如果原图不连通的话,不但生成树会建错,而且替换了边树也不连通);
原图连通,但删去这条边后没有边能替它连通两个块,此时树链剖分上询问这条边所得到的值应该是没更新过的初值,即inf。因此判断这个询问值如果是inf就输出Not connected即可。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 2147483647
#define N 50005
#define M 100005
using namespace std;
inline int read(){
int x=; bool f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=;
for(; isdigit(c);c=getchar()) x=(x<<)+(x<<)+(c^'');
if(f) return x;
return -x;
}
int n,m,q;
struct inedge{
int id,u,v,w;
bool operator <(const inedge &x)const{
return w<x.w;
}
}in_e[M],pre_e[M]; int head[M];
struct edge{
int v,w,next;
}e[N];
inline int add(int i,int u,int v,int w){
e[i].v=v, e[i].w=w, e[i].next=head[u], head[u]=i;
} int seg[N<<],tag[N<<];
int top[M],id[M],r_id[M],tot; //id是dfs序,r_id是dfs序号对应的节点编号 void pushdown(int o){
if(tag[o]==inf) return;
seg[o<<] = min(seg[o<<], tag[o]);
seg[o<<|] = min(seg[o<<|], tag[o]);
tag[o<<] = min(tag[o<<], tag[o]);
tag[o<<|] = min(tag[o<<|], tag[o]);
tag[o]=inf;
} void build(int l,int r,int o){
seg[o]=tag[o]=inf;
if(l==r) return; //in_e[edge_p[r_id[l]]>>1].w 这sb错误
int mid=(l+r)>>;
build(l,mid,o<<),
build(mid+,r,o<<|);
} void update(int l,int r,int o,int left,int right,int val){ if(l>=left && r<=right){
seg[o]=min(seg[o],val);
tag[o]=min(tag[o],val);
return;
}
pushdown(o);
int mid=(l+r)>>;
if(mid>=left) update(l,mid,o<<,left,right,val);
if(mid<right) update(mid+,r,o<<|,left,right,val);
} int query(int l,int r,int o,int left,int right){
if(l>=left && r<=right) return seg[o];
pushdown(o);
int mid=(l+r)>>, ret=inf;
if(mid>=left) ret = min(ret, query(l,mid,o<<,left,right));
if(mid<right) ret = min(ret, query(mid+,r,o<<|,left,right));
return ret;
} int fa[M],size[M],dep[M],hson[M];
void dfs1(int u,int f){
fa[u]=f, size[u]=;
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(f!=v){
dep[v]=dep[u]+;
dfs1(v,u);
if(hson[u]== || size[hson[u]]<size[v]) hson[u]=v;
size[u]+=size[v];
}
}
} void dfs2(int u,int anc){ //anc表示重链链顶
top[u]=anc, id[u]=tot, r_id[tot++]=u;
if(hson[u]==) return;
dfs2(hson[u],anc);
for(int i=head[u];i;i=e[i].next){
int v=e[i].v;
if(v!=fa[u] && v!=hson[u]) dfs2(v,v);
}
} void chain_update(int u,int v,int w){
int tu=top[u],tv=top[v];
while(tu!=tv){
if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
update(,n-,,id[tu],id[u],w);
u=fa[tu];
tu=top[u];
}
if(u==v) return;
if(dep[u]<dep[v]) swap(u,v);
update(,n-,,id[hson[v]],id[u],w);
} int chain_query(int u,int v){
int tu=top[u],tv=top[v];
if(tu!=tv){ //由于查询的是一条被断掉的边的两端点,因此这两点在原树上是相连的,只有当这条边是轻边的时候才会做这个
if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
return query(,n-,,id[tu],id[u]);
}
else{
if(dep[u]<dep[v]) swap(u,v);
return query(,n-,,id[hson[v]],id[u]);
}
} int bcj[N],cnt,sum;
bool istree[M],pre_istree[M];
int find(int x){return x==bcj[x] ? x : bcj[x]=find(bcj[x]);}
bool merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy) return ;
bcj[fy]=fx;
return ;
}
int main(){
//freopen("faq.in","r",stdin);
//freopen("count.out","w",stdout);
n=read(),m=read();
int i,u,v,w,Q,T,cx;
for(i=;i<=m;i++)
in_e[i].id=i, in_e[i].u=pre_e[i].u=read(), in_e[i].v=pre_e[i].v=read(), in_e[i].w=pre_e[i].w=read();
sort(in_e+,in_e+m+);
for(i=;i<=n;i++) bcj[i]=i; //预处理并查集
for(i=;i<=m;i++){ //最小生成树建出来,树链剖分用
if(merge(in_e[i].u, in_e[i].v)){ //两块未连通
add(i<< , in_e[i].u, in_e[i].v, in_e[i].w),
add(i<<|, in_e[i].v, in_e[i].u, in_e[i].w);
istree[i]=, pre_istree[in_e[i].id]=;
sum+=in_e[i].w;
if(++cnt==n-) break;
}
} if(cnt<n-){ //整张图不连通
Q=read();
for(i=;i<Q;i++){T=read(); printf("Not connected\n");}
return ;
} dep[]=;
dfs1(,); //建树链
dfs2(,); //分轻重链 build(,n-,);
for(i=;i<=m;i++)
if(!istree[i]) chain_update(in_e[i].u, in_e[i].v, in_e[i].w); Q=read();
for(i=;i<Q;i++){
T=read();
if(!pre_istree[T]) {printf("%d\n",sum); continue;}
int cx=chain_query(pre_e[T].u,pre_e[T].v);
if(cx==inf) printf("Not connected\n");
else printf("%d\n",sum-pre_e[T].w+cx);
}
return ;
}
【bzoj2238】Mst(树链剖分+线段树)的更多相关文章
- 【bzoj2238】Mst 最小生成树+树链剖分+线段树
题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...
- [HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]
题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树
[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...
- BZOJ2243 (树链剖分+线段树)
Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...
- POJ3237 (树链剖分+线段树)
Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...
- bzoj4034 (树链剖分+线段树)
Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...
- HDU4897 (树链剖分+线段树)
Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...
- Aizu 2450 Do use segment tree 树链剖分+线段树
Do use segment tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.bnuoj.com/v3/problem_show ...
- 【POJ3237】Tree(树链剖分+线段树)
Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...
随机推荐
- Luogu P4593 [TJOI2018]教科书般的亵渎
亵渎终于离开标准了,然而铺场快攻也变少了 给一个大力枚举(无任何性质)+艹出自然数幂和的方法,但是复杂度极限是\(O(k^4)\)的,不过跑的好快233 首先简单数学分析可以得出\(k=m+1\),因 ...
- MIPS汇编程序设计——四则运算计算器
实验目的 运用简单的MIPS实现一个能够整数加减乘除的计算器,同时使自己更加熟悉这些指令吧 MIPS代码 #sample example 'a small calculater’ # data sec ...
- NASM 之 helloworld1
SECTION .data msg: db "Hello World!", 0x0a len: equ $-msg SECTION .text global _main kerne ...
- abp viewmodel的写法
我的写法 public class QuotaCreateOrEditViewModel { public QuotaDto LoanQuota { get; set; } public bool I ...
- Airbnb:别抵制我,宝宝要过 10 岁生日
今日导读 喜欢旅游的你,一定听说或使用过 airbnb(爱彼迎),在出发前打开它,总是能通过它开始一段奇妙的旅行.可是最近,就在这个打着“共享家”概念的服务型网站正迎来十周年之际,它却遭到了很多国家的 ...
- OpenCascade:屏闪问题。
1.在OnDraw中同时调用用V3d_View::Redaw()和 V3d_View::FitAll();可暂时解决. 2.在OnDraw中同时调用用V3d_View::Update();
- java面试基础篇(二)
上一篇,我们说了一下线程和Map,或许那些太抽象,不太好理解,也不太方便记忆,我们这次说一些简单的. 1.Q:java的基本数据类型有哪些? A:四种整数类型(byte.short.int.long) ...
- js的正则表达式总结
1.8-20位数字 or 字母 or 特殊字符 var reg = /^[0-9a-zA-Z!@#$%^&*()_+-/.]{8,20}$/; 2.8-20位 数字+字母+特殊字符 //正则 ...
- awk纯干货
AWK的惊人表现: Awk设计的目的:简化一般文本处理的工作. 属于POSIX的一部分. AWK命令行: Awk的调用可以定义变量.提供程序并且指定输入文件: Awk [ -F fs ] [ -v ...
- 【计数】51nod1677 treecnt
要将答案看做是小问题的贡献和 Description 给定一棵n个节点的树,从1到n标号.选择k个点,你需要选择一些边使得这k个点通过选择的边联通,目标是使得选择的边数最少. 现需要计算对于所有选择k ...