2238: Mst

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 465  Solved: 131
[Submit][Status][Discuss]

Description

给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树。(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在)

Input

第一行两个正整数N,M$(N<=50000,M<=100000)$表示原图的顶点数和边数。
下面M行,每行三个整数X,Y,W描述了图的一条边$(X,Y)$,其边权为W$(W<=10000)$。保证两点之间至多只有一条边。
接着一行一个正整数Q,表示询问数。$(1<=Q<=100000)$
下面Q行,每行一个询问,询问中包含一个正整数T,表示把编号为T的边删掉(边从1到M按输入顺序编号)。

Output

Q行,对于每个询问输出对应最小生成树的边权和的值,如果图不连通则输出“Not connected”。

Sample Input

4 4
1 2 3
1 3 5
2 3 9
2 4 1
4
1
2
3
4

Sample Output

15
13
9
Not connected

数据规模

10%的数据$N,M,Q<=100$。
另外30%的数据,$N<=1000$。
100%的数据如题目。
 
第一眼看题以为是求最小生成树再加一条最小边,组成最小与次小生成树的合并(即n个点n条边),然后删的边如果在生成树里,就输出生成树的边权和减去这条边,如果不在生成树里,就输出最小生成树的边权和。
然而这个做法很快被 パス 掉了(bzoj的题显然没这么简单),因为删去一条树边仍然可能使原图不连通。因此,这题不是让求次小生成树。
经过dalao指导,发现这是数据结构板子题。
尽管这题不是让求次小生成树,但我们依然可以借鉴其某些思路。
我们先做好图的最小生成树。
如果删除的边不在最小生成树上,那答案没变,直接输出即可。
所以下文讨论的 均是删除的边在最小生成树上的情况。
此时要找能够代替 删去的边 的一条权值最小的边。用这条边代替删去的边后就是新的最小生成树了。
那怎么代替呢?
我们把最小生成树的样子画个图看看
我们容易发现它作为一棵树的性质:删去任意一条边只会把树分成两个连通块。我们要找的是所有 能使两个连通块连通的 非树边。这样的非树边就能够代替删去的边重新使树连通。
那怎么维护 能够代替删去的边的一条权值最小的边?(本题只要求最小生成树的权值和,因此可以只维护所需边的权值)
随便举个栗子:
红叉是删除的边,橙/绿线是非最小生成树的原图边。
很容易发现,这些橙边都可以成为代替边,而绿边都不可以成为代替边。直观原因是橙边都连接了两个连通块,而绿边都没有连接。
那反过来想,一条非树边可以代替哪些边?
找找简单情况(下面的图中黑边都是树边):
很明显,这条橙边可以代替两条黑边中的任意一条。
 
我们往左边拓展一点
 
也很明显,橙边只能代替右边两条黑边而不能代替最左边的黑边,绿边只能代替最左边的黑边而不能代替右边两条黑边。

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

证明:

以这个图为例,一条非树边与其两端点在树上的简单路径组成的是一个x个点x条边的环,而这x个点可以删去其中一条边,只用x-1条边连通,因此这条非树边可以代替简单路径中的任意一条树边。

相反,这条非树边与 不在上述简单路径中的树边 只能形成x个点x-1条边的树,删去任意一条边都会使这棵树不连通,而这棵树作为原最小生成树的一棵子树,这棵子树不连通的话原最小生成树一定也不连通。所以这条非树边不能代替任意一条非简单路径上的边。

如何快速处理树上的简单路径?树链剖分!

所以算法明确了:先预处理出最小生成树,然后将这棵最小生成树进行树链剖分,每一条边记录能够代替它的、权值最小的非树边的长度。对于每条非树边,利用树剖快速更新其两端点在树上的简单路径之间的所有边记录的最小值。查询时直接查询删除的边上存储的最小值即可。

实际代码可以将边的信息存在其儿子节点上,简化代码复杂度

有两种情况要特判Not connected:

原图不连通,对于所有询问都输出Not connected(树链剖分只是维护用其它边替换一条边的情况,如果原图不连通的话,不但生成树会建错,而且替换了边树也不连通);

原图连通,但删去这条边后没有边能替它连通两个块,此时树链剖分上询问这条边所得到的值应该是没更新过的初值,即inf。因此判断这个询问值如果是inf就输出Not connected即可。

  1. #include<cstdio>
  2. #include<cstring>
  3. #include<iostream>
  4. #include<algorithm>
  5. #define inf 2147483647
  6. #define N 50005
  7. #define M 100005
  8. using namespace std;
  9. inline int read(){
  10. int x=; bool f=; char c=getchar();
  11. for(;!isdigit(c);c=getchar()) if(c=='-') f=;
  12. for(; isdigit(c);c=getchar()) x=(x<<)+(x<<)+(c^'');
  13. if(f) return x;
  14. return -x;
  15. }
  16. int n,m,q;
  17. struct inedge{
  18. int id,u,v,w;
  19. bool operator <(const inedge &x)const{
  20. return w<x.w;
  21. }
  22. }in_e[M],pre_e[M];
  23.  
  24. int head[M];
  25. struct edge{
  26. int v,w,next;
  27. }e[N];
  28. inline int add(int i,int u,int v,int w){
  29. e[i].v=v, e[i].w=w, e[i].next=head[u], head[u]=i;
  30. }
  31.  
  32. int seg[N<<],tag[N<<];
  33. int top[M],id[M],r_id[M],tot; //id是dfs序,r_id是dfs序号对应的节点编号
  34.  
  35. void pushdown(int o){
  36. if(tag[o]==inf) return;
  37. seg[o<<] = min(seg[o<<], tag[o]);
  38. seg[o<<|] = min(seg[o<<|], tag[o]);
  39. tag[o<<] = min(tag[o<<], tag[o]);
  40. tag[o<<|] = min(tag[o<<|], tag[o]);
  41. tag[o]=inf;
  42. }
  43.  
  44. void build(int l,int r,int o){
  45. seg[o]=tag[o]=inf;
  46. if(l==r) return; //in_e[edge_p[r_id[l]]>>1].w 这sb错误
  47. int mid=(l+r)>>;
  48. build(l,mid,o<<),
  49. build(mid+,r,o<<|);
  50. }
  51.  
  52. void update(int l,int r,int o,int left,int right,int val){
  53.  
  54. if(l>=left && r<=right){
  55. seg[o]=min(seg[o],val);
  56. tag[o]=min(tag[o],val);
  57. return;
  58. }
  59. pushdown(o);
  60. int mid=(l+r)>>;
  61. if(mid>=left) update(l,mid,o<<,left,right,val);
  62. if(mid<right) update(mid+,r,o<<|,left,right,val);
  63. }
  64.  
  65. int query(int l,int r,int o,int left,int right){
  66. if(l>=left && r<=right) return seg[o];
  67. pushdown(o);
  68. int mid=(l+r)>>, ret=inf;
  69. if(mid>=left) ret = min(ret, query(l,mid,o<<,left,right));
  70. if(mid<right) ret = min(ret, query(mid+,r,o<<|,left,right));
  71. return ret;
  72. }
  73.  
  74. int fa[M],size[M],dep[M],hson[M];
  75. void dfs1(int u,int f){
  76. fa[u]=f, size[u]=;
  77. for(int i=head[u];i;i=e[i].next){
  78. int v=e[i].v;
  79. if(f!=v){
  80. dep[v]=dep[u]+;
  81. dfs1(v,u);
  82. if(hson[u]== || size[hson[u]]<size[v]) hson[u]=v;
  83. size[u]+=size[v];
  84. }
  85. }
  86. }
  87.  
  88. void dfs2(int u,int anc){ //anc表示重链链顶
  89. top[u]=anc, id[u]=tot, r_id[tot++]=u;
  90. if(hson[u]==) return;
  91. dfs2(hson[u],anc);
  92. for(int i=head[u];i;i=e[i].next){
  93. int v=e[i].v;
  94. if(v!=fa[u] && v!=hson[u]) dfs2(v,v);
  95. }
  96. }
  97.  
  98. void chain_update(int u,int v,int w){
  99. int tu=top[u],tv=top[v];
  100. while(tu!=tv){
  101. if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
  102. update(,n-,,id[tu],id[u],w);
  103. u=fa[tu];
  104. tu=top[u];
  105. }
  106. if(u==v) return;
  107. if(dep[u]<dep[v]) swap(u,v);
  108. update(,n-,,id[hson[v]],id[u],w);
  109. }
  110.  
  111. int chain_query(int u,int v){
  112. int tu=top[u],tv=top[v];
  113. if(tu!=tv){ //由于查询的是一条被断掉的边的两端点,因此这两点在原树上是相连的,只有当这条边是轻边的时候才会做这个
  114. if(dep[tu]<dep[tv]) swap(tu,tv), swap(u,v);
  115. return query(,n-,,id[tu],id[u]);
  116. }
  117. else{
  118. if(dep[u]<dep[v]) swap(u,v);
  119. return query(,n-,,id[hson[v]],id[u]);
  120. }
  121. }
  122.  
  123. int bcj[N],cnt,sum;
  124. bool istree[M],pre_istree[M];
  125. int find(int x){return x==bcj[x] ? x : bcj[x]=find(bcj[x]);}
  126. bool merge(int x,int y){
  127. int fx=find(x),fy=find(y);
  128. if(fx==fy) return ;
  129. bcj[fy]=fx;
  130. return ;
  131. }
  132. int main(){
  133. //freopen("faq.in","r",stdin);
  134. //freopen("count.out","w",stdout);
  135. n=read(),m=read();
  136. int i,u,v,w,Q,T,cx;
  137. for(i=;i<=m;i++)
  138. 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();
  139. sort(in_e+,in_e+m+);
  140. for(i=;i<=n;i++) bcj[i]=i; //预处理并查集
  141. for(i=;i<=m;i++){ //最小生成树建出来,树链剖分用
  142. if(merge(in_e[i].u, in_e[i].v)){ //两块未连通
  143. add(i<< , in_e[i].u, in_e[i].v, in_e[i].w),
  144. add(i<<|, in_e[i].v, in_e[i].u, in_e[i].w);
  145. istree[i]=, pre_istree[in_e[i].id]=;
  146. sum+=in_e[i].w;
  147. if(++cnt==n-) break;
  148. }
  149. }
  150.  
  151. if(cnt<n-){ //整张图不连通
  152. Q=read();
  153. for(i=;i<Q;i++){T=read(); printf("Not connected\n");}
  154. return ;
  155. }
  156.  
  157. dep[]=;
  158. dfs1(,); //建树链
  159. dfs2(,); //分轻重链
  160.  
  161. build(,n-,);
  162. for(i=;i<=m;i++)
  163. if(!istree[i]) chain_update(in_e[i].u, in_e[i].v, in_e[i].w);
  164.  
  165. Q=read();
  166. for(i=;i<Q;i++){
  167. T=read();
  168. if(!pre_istree[T]) {printf("%d\n",sum); continue;}
  169. int cx=chain_query(pre_e[T].u,pre_e[T].v);
  170. if(cx==inf) printf("Not connected\n");
  171. else printf("%d\n",sum-pre_e[T].w+cx);
  172. }
  173. return ;
  174. }

【bzoj2238】Mst(树链剖分+线段树)的更多相关文章

  1. 【bzoj2238】Mst 最小生成树+树链剖分+线段树

    题目描述 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在) 输入 第一行两 ...

  2. [HDU3710] Battle Over Cities [树链剖分+线段树+并查集+kruskal+思维]

    题面 一句话题意: 给定一张 N 个点, M 条边的无向连通图, 每条边上有边权 w . 求删去任意一个点后的最小生成树的边权之和. 思路 首先肯定要$kruskal$一下 考虑$MST$里面去掉一个 ...

  3. 【BZOJ-2325】道馆之战 树链剖分 + 线段树

    2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 1153  Solved: 421[Submit][Statu ...

  4. 【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

    [BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的 ...

  5. BZOJ2243 (树链剖分+线段树)

    Problem 染色(BZOJ2243) 题目大意 给定一颗树,每个节点上有一种颜色. 要求支持两种操作: 操作1:将a->b上所有点染成一种颜色. 操作2:询问a->b上的颜色段数量. ...

  6. POJ3237 (树链剖分+线段树)

    Problem Tree (POJ3237) 题目大意 给定一颗树,有边权. 要求支持三种操作: 操作一:更改某条边的权值. 操作二:将某条路径上的边权取反. 操作三:询问某条路径上的最大权值. 解题 ...

  7. bzoj4034 (树链剖分+线段树)

    Problem T2 (bzoj4034 HAOI2015) 题目大意 给定一颗树,1为根节点,要求支持三种操作. 操作 1 :把某个节点 x 的点权增加 a . 操作 2 :把某个节点 x 为根的子 ...

  8. HDU4897 (树链剖分+线段树)

    Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作 ...

  9. 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 ...

  10. 【POJ3237】Tree(树链剖分+线段树)

    Description You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edg ...

随机推荐

  1. 通过StringBuilder的reverse()实现倒序

    import java.util.Scanner; public class ReverseString { public static void main(String[] args) { Scan ...

  2. UVA1660 Cable TV Network (无向图的点连通度)

    题意:求一个无向图的点连通度. 把一个点拆成一个入点和一个出点,之间连一条容量为1的有向边,表示能被用一次.最大流求最小割即可. 一些细节的东西:1.源点固定,汇点要枚举一遍,因为最小割割断以后会形成 ...

  3. bzoj 2658

    首先考虑容斥 我们计算出所有没有点在其中的矩形,然后用所有矩形减去这些矩形即可 然后考虑如何计算没有点在其中的矩形 采用扫描线的思想,从上向下一行一行扫,假设我们扫到的行编号是$a$,然后考虑如果左右 ...

  4. Maven归纳

      一.常用功能 1.Maven的中央仓库 https://mvnrepository.com/ 2.添加jar包依赖 1.首先点击pom.xml,然后点击弹出页面中的Dependencies选项,接 ...

  5. AppCrawler自动化遍历使用详解(版本2.1.0 )(转)

    AppCrawle是自动遍历的app爬虫工具,最大的特点是灵活性,实现:对整个APP的所有可点击元素进行遍历点击.   优点: 1.支持android和iOS, 支持真机和模拟器 2.可通过配置来设定 ...

  6. 洛谷 P1126 机器人搬重物 (BFS)

    题目链接:https://www.luogu.org/problemnew/show/P1126 吐槽:这题很阴险 一开始没把格子图转化成点图:30分 转化成点图,发现样例过不去,原来每步要判断vis ...

  7. please upgrade your plan to create a new private reposiory

    请升级你的计划来创建一个新的私人仓库 提交仓库到github,要公开,除非买他们服务,所以把勾去掉就好了keep this code private

  8. shell脚本,awk实现每个数字加1.

    [root@localhost add]# cat file [root@localhost add]# cat file|awk '{for(i=1;i<=NF;i++){$i+=1}}1' ...

  9. centos7 samba配置完成后不管怎么登陆都会显示密码错误的解决方案

    添加系统用户 useradd samba 添加samba用户 smbpasswd -a samba 激活samba用户 smbpasswd -e samba 1.win+r运行secpol.msc打开 ...

  10. sha1、base64、ase加密

    <!DOCTYPE html><html><head><title>sha1.base64.ase加密</title><meta ch ...