CF827D Best Edge Weight[最小生成树+树剖/LCT/(可并堆/set启发式合并+倍增)]

题意:一张图求每条边边权最多改成多少可以让所有MST都包含这条边。


这题还是要考察Kruskal的贪心过程。

先跑一棵MST出来。然后考虑每条边。

如果他是非树边,要让他Kruskal的时候被选入,必须要让他连的两个点$u,v$连通之前被选上,也就是说,必须得小于MST上$u,v$路径中的至少一条边,那么让他小于最大的那条(减一)即可。

如果他是树边,那么考虑如果删去他,他连接的两点如果要连通,可否用其他边替换。发现一定可以用经过这条边的非树边替换他,且会使用最小的一条非树边作为新的MST的边。所以只要找到路径覆盖这条边的所有非树边的最小的减一即可。

综上,我们需要做树上链查询$max$,链取$min$的操作,并且这两个操作相互独立。我一开始写了一个倍增,结果欠思考,在取$min$操作上卡住了。。所以我重新写了一个树剖分别维护两个信息。复杂度$O(nlog^2 n)$,吊打单$log$。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<queue>
  7 #define dbg(x) cerr << #x << " = " << x <<endl
  8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
  9 using namespace std;
 10 typedef long long ll;
 11 typedef double db;
 12 typedef pair<int,int> pii;
 13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
 14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
 15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
 16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
 17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
 18 template<typename T>inline T read(T&x){
 19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
 20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
 21 }
 22 const int N=2e5+7,INF=0x3f3f3f3f;
 23 struct thxorz{int to,nxt,w;}G[N<<1];
 24 int Head[N],tot=1;
 25 int ans[N],used[N];
 26 int n,m;
 27 inline void Addedge(int x,int y,int z){
 28     G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;
 29     G[++tot].to=x,G[tot].nxt=Head[y],Head[y]=tot,G[tot].w=z;
 30 }
 31 int fa[N],topfa[N],dep[N],son[N],sum[N],pos[N],A[N],cnt;
 32 #define y G[j].to
 33 void dfs1(int x,int f){
 34     fa[x]=f;dep[x]=dep[G[f^1].to]+1;sum[x]=1;int tmp=-1;
 35     for(register int j=Head[x];j;j=G[j].nxt)if((j^1)^f)dfs1(y,j),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y);
 36 }
 37 void dfs2(int x,int topf){
 38     topfa[x]=topf;pos[x]=cnt,A[cnt++]=G[fa[x]].w;if(!son[x])return;dfs2(son[x],topf);
 39     for(register int j=Head[x];j;j=G[j].nxt)if((j^1)^fa[x]&&y^son[x])dfs2(y,y);
 40 }
 41 #undef y
 42 struct segment_tree{
 43     int maxv[N<<2],cov[N<<2],tag[N<<2];
 44     #define lc i<<1
 45     #define rc i<<1|1
 46     segment_tree(){memset(cov,0x3f,sizeof cov),memset(tag,0x3f,sizeof tag);}
 47     inline void pushdown(int i){
 48         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;}
 49     }
 50     void Build(int i,int L,int R){
 51         if(L==R){maxv[i]=A[L];return;}
 52         Build(lc,L,L+R>>1),Build(rc,(L+R>>1)+1,R);maxv[i]=_max(maxv[lc],maxv[rc]);
 53     }
 54     int UPD(int i,int L,int R,int ql,int qr,int val){
 55         if(ql<=L&&qr>=R){MIN(cov[i],val);MIN(tag[i],val);return maxv[i];}
 56         int mid=L+R>>1,ret=0;pushdown(i);
 57         if(ql<=mid)MAX(ret,UPD(lc,L,mid,ql,qr,val));
 58         if(qr>mid)MAX(ret,UPD(rc,mid+1,R,ql,qr,val));
 59         cov[i]=_min(cov[lc],cov[rc]);return ret;
 60     }
 61     int Query_cover(int i,int L,int R,int x){
 62         if(L==R)return cov[i];
 63         int mid=L+R>>1;pushdown(i);
 64         if(x<=mid)return Query_cover(lc,L,mid,x);
 65         return Query_cover(rc,mid+1,R,x);
 66     }
 67 }T;
 68 
 69 struct wphorz{
 70     int u,v,w,id;
 71     inline bool operator <(const wphorz&A)const{return w<A.w;}
 72 }e[N];
 73 struct dsu{
 74     int anc[N];
 75     inline void Clear(){for(register int i=1;i<=n;++i)anc[i]=i;}
 76     inline int Find(int x){return anc[x]==x?x:anc[x]=Find(anc[x]);}
 77 }S;
 78 inline void Kruskal(){
 79     sort(e+1,e+m+1);S.Clear();//for(register int i=1;i<=m;++i)printf("%d %d %d
",e[i].u,e[i].v,e[i].w);    
 80     for(register int i=1;i<=m;++i)if(S.Find(e[i].u)^S.Find(e[i].v))
 81         S.anc[S.anc[e[i].u]]=S.anc[e[i].v],Addedge(e[i].u,e[i].v,e[i].w),used[i]=1;
 82 }
 83 inline int qry_and_upd(int x,int y,int val){
 84     int ret=0;
 85     while(topfa[x]^topfa[y]){
 86         if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y);
 87         MAX(ret,T.UPD(1,1,n-1,pos[topfa[x]],pos[x],val));
 88         x=G[fa[topfa[x]]^1].to;
 89     }
 90     if(dep[x]>dep[y])_swap(x,y);
 91     return _max(ret,x==y?0:T.UPD(1,1,n-1,pos[x]+1,pos[y],val));
 92 }
 93 inline void process(){
 94     for(register int i=1;i<=m;++i)if(!used[i])ans[e[i].id]=qry_and_upd(e[i].u,e[i].v,e[i].w)-1;
 95     for(register int i=1;i<=m;++i)if(used[i]){//printf("%d %d
",e[i].id,e[i].w);
 96         if(dep[e[i].u]<dep[e[i].v])_swap(e[i].u,e[i].v);
 97         ans[e[i].id]=T.Query_cover(1,1,n-1,pos[e[i].u]);
 98         ans[e[i].id]<INF?ans[e[i].id]--:ans[e[i].id]=-1;
 99     }//puts("");
100     for(register int i=1;i<=m;++i)printf("%d ",ans[i]);puts("");
101 }
102 
103 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
104     read(n),read(m);
105     for(register int i=1;i<=m;++i)read(e[i].u),read(e[i].v),read(e[i].w),e[i].id=i;
106     Kruskal();dfs1(1,0),dfs2(1,1);
107     T.Build(1,1,n-1);process();
108     return 0;
109 }
View Code

事实上,倍增那种方法也不是不可以,只不过取$min$操作只要离线在节点上打标记,$x,y$处标记插入值,$lca$处标记删除之,然后用set维护最小值,dfs自下而上启发式合并更新即可。复杂度$O(nlog^2 n)$。还可以把set改成可并堆,然后删除的话用懒惰删除法(见lyd书),然后做到一个$log$。

另外一种方法,直接LCT。

总结:掌握好Kruskal本质是关键。

原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11691260.html