HNOI2010 城市建设
很妙的题目。
首先可以将询问给离线下来,将一条边的边权更改变为在
然后我们发现要求的就是每个时间的最小生成树,那我们如何优化复杂度呢?
我们有一个容易想到的方法:
-
对于时间段
,设这段时间中未被更改的边集为 ,那么若只对 中的边做最小生成树(kruskal),如果边 未被加入到树边当中,那么这条边一定不会被选择; -
如果让其他不在
中的边(也就是被改动过的边)先进行 kruskal,然后再对 中的边尝试加入树边,那如果边 被加入到最小生成树中,就说明这条边必然会被加入。然后把 中不属于这两种的边继续往下递归。
貌似这个做法很玄乎,但经过证明,我们发现这样做是对的,以下为证明:
操作二使得连通块个数不会超过
考虑使用线段树和可撤销并查集维护即可。
点击查看代码
#include<bits/stdc++.h> #define fir first #define sec second #define int long long #define lowbit(x) x&(-x) #define mkp(a,b) make_pair(a,b) using namespace std; typedef pair<int,int> pir; inline int read(){ int x=0,f=1; char c=getchar(); while(!isdigit(c)){if(c=='-') f=-1; c=getchar();} while(isdigit(c)){x=x*10+(c^48); c=getchar();} return x*f; } const int inf=1e18,N=5e4+5; int n,m,q; struct dsu{int a,b,val;}st[N]; int fa[N],siz[N],top,res; inline int getfa(int x){ if(fa[x]!=x) return getfa(fa[x]); return fa[x]; } inline bool merge(int u,int v,int w){ int a=getfa(u),b=getfa(v); if(a==b) return 0; if(siz[a]<siz[b]) swap(a,b); siz[a]+=siz[b],fa[b]=a,res+=w; st[++top]=dsu{a,b,w}; return 1; } inline void del(){ auto [a,b,val]=st[top]; siz[a]-=siz[b],fa[b]=b,res-=val; top--; } struct edge{int u,v,w;}s[N]; inline bool cmp(edge a,edge b){return a.w<b.w;} struct tree{ vector<edge> e[N<<2]; inline void add(int l,int r,int p,int ll,int rr,edge k){ if(ll>rr) return ; if(ll<=l&&r<=rr){ e[p].push_back(k); return ; } int mid=(l+r)>>1; if(ll<=mid) add(l,mid,p<<1,ll,rr,k); if(rr>mid) add(mid+1,r,p<<1|1,ll,rr,k); } inline void solve(int l,int r,int p){ int T=top; sort(e[p].begin(),e[p].end(),cmp); if(l==r){ for(auto [u,v,w]:e[p]) merge(u,v,w); cout<<res<<'\n'; while(top>T) del(); assert(top==T); return ; } int sz=e[p].size(); vector<int> vv; vv.resize(sz); for(int i=0;i<sz;i++) vv[i]=0; for(int i=0;i<sz;i++){ auto [u,v,w]=e[p][i]; if(!merge(u,v,0)) vv[i]=1; } while(top>T) del(); for(int i=l;i<=r;i++){ auto [u,v,w]=s[i]; merge(u,v,0); } for(int i=0;i<sz;i++){ if(vv[i]) continue; auto [u,v,w]=e[p][i]; if(merge(u,v,0)) vv[i]=2; } while(top>T) del(); for(int i=0;i<sz;i++){ auto [u,v,w]=e[p][i]; if(vv[i]==2) merge(u,v,w); if(vv[i]==0){ e[p<<1].push_back(e[p][i]); e[p<<1|1].push_back(e[p][i]); } } int mid=(l+r)>>1; solve(l,mid,p<<1); solve(mid+1,r,p<<1|1); while(top>T) del(); } }Tr; int lst[N]; int u[N],v[N],w[N]; signed main(){ n=read(),m=read(),q=read(); for(int i=1;i<=m;i++) u[i]=read(),v[i]=read(),w[i]=read(),lst[i]=1; for(int i=1;i<=q;i++){ int id=read(),x=read(); if(i>1) Tr.add(1,q,1,lst[id],i-1,edge{u[id],v[id],w[id]}); lst[id]=i,w[id]=x; s[i]=edge{u[id],v[id],w[id]}; } for(int i=1;i<=m;i++) Tr.add(1,q,1,lst[i],q,edge{u[i],v[i],w[i]}); for(int i=1;i<=n;i++) siz[i]=1,fa[i]=i; Tr.solve(1,q,1); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现