CF827D Best Edge Weight[最小生成树+树剖/LCT/(可并堆/set启发式合并+倍增)]
题意:一张图求每条边边权最多改成多少可以让所有MST都包含这条边。
这题还是要考察Kruskal的贪心过程。
先跑一棵MST出来。然后考虑每条边。
如果他是非树边,要让他Kruskal的时候被选入,必须要让他连的两个点$u,v$连通之前被选上,也就是说,必须得小于MST上$u,v$路径中的至少一条边,那么让他小于最大的那条(减一)即可。
如果他是树边,那么考虑如果删去他,他连接的两点如果要连通,可否用其他边替换。发现一定可以用经过这条边的非树边替换他,且会使用最小的一条非树边作为新的MST的边。所以只要找到路径覆盖这条边的所有非树边的最小的减一即可。
综上,我们需要做树上链查询$\max$,链取$\min$的操作,并且这两个操作相互独立。我一开始写了一个倍增,结果欠思考,在取$\min$操作上卡住了。。所以我重新写了一个树剖分别维护两个信息。复杂度$O(n\log^2 n)$,吊打单$\log$。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=2e5+,INF=0x3f3f3f3f;
struct thxorz{int to,nxt,w;}G[N<<];
int Head[N],tot=;
int ans[N],used[N];
int n,m;
inline void Addedge(int x,int y,int z){
G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;
G[++tot].to=x,G[tot].nxt=Head[y],Head[y]=tot,G[tot].w=z;
}
int fa[N],topfa[N],dep[N],son[N],sum[N],pos[N],A[N],cnt;
#define y G[j].to
void dfs1(int x,int f){
fa[x]=f;dep[x]=dep[G[f^].to]+;sum[x]=;int tmp=-;
for(register int j=Head[x];j;j=G[j].nxt)if((j^)^f)dfs1(y,j),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y);
}
void dfs2(int x,int topf){
topfa[x]=topf;pos[x]=cnt,A[cnt++]=G[fa[x]].w;if(!son[x])return;dfs2(son[x],topf);
for(register int j=Head[x];j;j=G[j].nxt)if((j^)^fa[x]&&y^son[x])dfs2(y,y);
}
#undef y
struct segment_tree{
int maxv[N<<],cov[N<<],tag[N<<];
#define lc i<<1
#define rc i<<1|1
segment_tree(){memset(cov,0x3f,sizeof cov),memset(tag,0x3f,sizeof tag);}
inline void pushdown(int i){
if(tag[i]<INF){MIN(tag[lc],tag[i]),MIN(tag[rc],tag[i]);MIN(cov[lc],tag[i]),MIN(cov[rc],tag[i]);tag[i]=INF;}
}
void Build(int i,int L,int R){
if(L==R){maxv[i]=A[L];return;}
Build(lc,L,L+R>>),Build(rc,(L+R>>)+,R);maxv[i]=_max(maxv[lc],maxv[rc]);
}
int UPD(int i,int L,int R,int ql,int qr,int val){
if(ql<=L&&qr>=R){MIN(cov[i],val);MIN(tag[i],val);return maxv[i];}
int mid=L+R>>,ret=;pushdown(i);
if(ql<=mid)MAX(ret,UPD(lc,L,mid,ql,qr,val));
if(qr>mid)MAX(ret,UPD(rc,mid+,R,ql,qr,val));
cov[i]=_min(cov[lc],cov[rc]);return ret;
}
int Query_cover(int i,int L,int R,int x){
if(L==R)return cov[i];
int mid=L+R>>;pushdown(i);
if(x<=mid)return Query_cover(lc,L,mid,x);
return Query_cover(rc,mid+,R,x);
}
}T; struct wphorz{
int u,v,w,id;
inline bool operator <(const wphorz&A)const{return w<A.w;}
}e[N];
struct dsu{
int anc[N];
inline void Clear(){for(register int i=;i<=n;++i)anc[i]=i;}
inline int Find(int x){return anc[x]==x?x:anc[x]=Find(anc[x]);}
}S;
inline void Kruskal(){
sort(e+,e+m+);S.Clear();//for(register int i=1;i<=m;++i)printf("%d %d %d\n",e[i].u,e[i].v,e[i].w);
for(register int i=;i<=m;++i)if(S.Find(e[i].u)^S.Find(e[i].v))
S.anc[S.anc[e[i].u]]=S.anc[e[i].v],Addedge(e[i].u,e[i].v,e[i].w),used[i]=;
}
inline int qry_and_upd(int x,int y,int val){
int ret=;
while(topfa[x]^topfa[y]){
if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y);
MAX(ret,T.UPD(,,n-,pos[topfa[x]],pos[x],val));
x=G[fa[topfa[x]]^].to;
}
if(dep[x]>dep[y])_swap(x,y);
return _max(ret,x==y?:T.UPD(,,n-,pos[x]+,pos[y],val));
}
inline void process(){
for(register int i=;i<=m;++i)if(!used[i])ans[e[i].id]=qry_and_upd(e[i].u,e[i].v,e[i].w)-;
for(register int i=;i<=m;++i)if(used[i]){//printf("%d %d\n",e[i].id,e[i].w);
if(dep[e[i].u]<dep[e[i].v])_swap(e[i].u,e[i].v);
ans[e[i].id]=T.Query_cover(,,n-,pos[e[i].u]);
ans[e[i].id]<INF?ans[e[i].id]--:ans[e[i].id]=-;
}//puts("");
for(register int i=;i<=m;++i)printf("%d ",ans[i]);puts("");
} int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
read(n),read(m);
for(register int i=;i<=m;++i)read(e[i].u),read(e[i].v),read(e[i].w),e[i].id=i;
Kruskal();dfs1(,),dfs2(,);
T.Build(,,n-);process();
return ;
}
事实上,倍增那种方法也不是不可以,只不过取$\min$操作只要离线在节点上打标记,$x,y$处标记插入值,$lca$处标记删除之,然后用set维护最小值,dfs自下而上启发式合并更新即可。复杂度$O(n\log^2 n)$。还可以把set改成可并堆,然后删除的话用懒惰删除法(见lyd书),然后做到一个$\log$。
另外一种方法,直接LCT。
总结:掌握好Kruskal本质是关键。
CF827D Best Edge Weight[最小生成树+树剖/LCT/(可并堆/set启发式合并+倍增)]的更多相关文章
- 【bzoj3510】首都 LCT维护子树信息(+启发式合并)
题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...
- cf827D Best Edge Weight (kruskal+倍增lca+并查集)
先用kruskal处理出一个最小生成树 对于非树边,倍增找出两端点间的最大边权-1就是答案 对于树边,如果它能被替代,就要有一条非树边,两端点在树上的路径覆盖了这条树边,而且边权不大于这条树边 这里可 ...
- 【CodeForces】827 D. Best Edge Weight 最小生成树+倍增LCA+并查集
[题目]D. Best Edge Weight [题意]给定n个点m条边的带边权无向连通图,对每条边求最大边权,满足其他边权不变的前提下图的任意最小生成树都经过它.n,m<=2*10^5,1&l ...
- 4.17 省选模拟赛 远行 LCT 启发式合并 倍增
容易写出nQ的暴力 由于数据是期望的时间 所以直接dfs可以跑的很快 可以拿到70分. 当然 可以进一步优化暴力 使用换根dp 然后可以将暴力优化到n^2. const int MAXN=300010 ...
- [Bzoj2243][SDOI2011]染色(线段树&&树剖||LCT)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2243 线段树+树链剖分,在线段树需要每次用lt和rt两个数组记录当前区间的左右边界的颜色 ...
- CF827D Best Edge Weight 题解
题意: 给定一个点数为 n,边数为 m,权值不超过 \(10^9\) 的带权连通图,没有自环与重边. 现在要求对于每一条边求出,这条边的边权最大为多少时,它还能出现在所有可能的最小生成树上,如果对于任 ...
- BZOJ 1576 树剖+LCT
题意:给定一张图,保证 $1$ 号点到其他所有点的最短路径是唯一的,求:对于点 $i$,不经过 $1$ 到 $i$ 最短路径上最后一条边的最短路. 题解:可以先建出最短路树,然后枚举每一条非树边. 对 ...
- BZOJ2238 Mst[最小生成树+树剖+线段树]
跑一遍mst.对于非mst上的边,显然删掉不影响. 如果删边在树上,相当于这时剩下两个连通块.可以证明要重新构成mst只需要再加一条连接两个连通块的最小边,不会证,yy一下,因为原来连通块连的边权和已 ...
- Edge Weight Assignment(树-异或-贪心)
大意: 给定一棵无根树,要求你任意设置n-1条边的边权. 使得任意叶子节点间边权的XOR值为0: 此时,令f为所有边权数值不同的个数,求最小的f和最大的f. \(\color{Red}{------- ...
随机推荐
- 2019秋JAVA第三周课程总结及实验报告(二)
个人博客 一.基础字符串操作 题目:已知字符串:"this is a test of java".按要求执行以下操作:(要求源代码.结果截图.) 统计该字符串中字母s出现的次数. ...
- 【持续更新】一个简洁、易用的美赛 LaTeX 模板: easyMCM
目录 1 当前美赛模板通行情况的概述 2 EasyMCM 宏包说明 2.1 与 mcmthesis 的关系之说明 2.2 easymcm宏包的简介 2.3 美赛模板下载地址 3 README 摘录 3 ...
- SQL SERVER导入EXCEL文件:无法创建链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.Ace.OLEDB.12.0" 的实例。
[方法一] --开启导入功能 exec sp_configure 'show advanced options',1 reconfigure exec sp_configure 'A ...
- Kettle无法打开文件资源库
问题: Kettle无法打开文件资源库. 问题描述: 新建文件资源库之后,资源库路径中有中文路径.退出kettle之后,再次进去发现没有了右上角的connect按钮了. 原因: kettle的repo ...
- 记录一次线上yarn RM频繁切换的故障
周末一大早被报警惊醒,rm频繁切换 急急忙忙排查 看到两处错误日志 错误信息1 ervation <memory:0, vCores:0> 2019-12-21 11:51:57,781 ...
- java版MD5签名工具类
package com.net.util; import java.security.MessageDigest; /** * MD5签名工具类 * @author zhangdi * */ publ ...
- Python学习2——使用字符串(完整版)
""" 在C语言入门的时候字符串没有好好学习,导致后期语言根本没有入门, 更导致之后大量的codeing时间浪费,效率低下. 因此,借助这次Python入门,好好地将字符 ...
- Centos7搭建在线yum源
1: 首先关闭防护墙或者设置规则通过且关闭selinux Systemctl diablefirewalld 永久关闭防火墙 vim/etc/sysconfig/selinux 并修改SELINUX ...
- docker 入门6 - 部署 【翻译】
开始,第 6 部分:部署应用 先决条件 安装 Docker. 获取第 3 部分先决条件中所述的 Docker Compose. 获取 Docker Machine,如第 4 部分先决条件中所述. 阅读 ...
- C# DataTable根据字段排序
DataTable dt = new DataTable(); dt.Columns.Add("Name"); dt.Columns.Add("Age");// ...