[JOI 2026 SemiFinal] 新的桥梁 / New Bridge
给一个 n 个点 m 条边的带权图。
定义 Ds,i 表示以 s 作为起点执行 Prim 最小生成树算法,i 是第几个被扩展到的。
多次询问,每次给定 x,求 s∑Ds,x。
n≤3×105,m≤6×105。
所有边边权互不相同。
首先给出一个样例:
1 2 3 4 5 6
| 4 5 1 3 1 2 3 2 3 4 3 4 2 4 1 4 5
|
可以先模拟以下,以加深对题解的理解。
由于 Prim 算法求出的最小生成树和 Kruskal 一致,不妨考虑 Kruskal 以及 Kruskal 重构树。
对于 Kruskal 重构树上的一个非叶子节点,其代表一条边,连接两个连通块 S1,S2,那么所有会先到 S1 的节点必然会至少为 S2 中的每一个节点添加 ∣S1∣ 的贡献(因为根据 Kurskal 的过程,他必须先把 S1 中的点走完)。同理可的先到 S2 中的点到 S1 必然会产生 ∣S2∣ 的贡献。
因此我们就有一个基本的算法框架(伪代码):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def Solve(): while can_Kruskal(): EDGE edge=Kruskal_edge() def u,v [u,v]=edge u=getf(u) v=getf(v) def szu,szv [szu,szv]=[get_sz(u),get_sz(v)] def totu,totv [totu,totv]=[first_to(u),first_to(v)] subtree_add(u,totv*szv) subtree_add(v,totu*szu) while Q--: def x; x=get_input() output(ans[x]) exit
|
所以现在的问题就是:如何求出有多少个点会先到 S1,S2?
不妨先建立最小生成树,然后我们会发现:设连接 S1,S2 的点分别是 u,v,那么有一个显然的事实:
最小生成树上,v 那一侧的点作为起点一定先到 S2,u 那一侧的起点一定先到 S1。
于是先建立最小生成树,然后记录子树大小即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <bits/stdc++.h> using namespace std; const int N=600010; using ll=long long; ll ans[N]; int n,m,Q,tot[N],f[N],sz[N],F[N]; struct node{int u,v,w;}q[N]; bool operator<(node x,node y){return x.w<y.w;} vector<int>v[N]; int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);} void kruskal(){ for(int i=1;i<=n;i++)f[i]=i; sort(q+1,q+1+m); for(int i=1;i<=m;i++){ int x=getf(q[i].u),y=getf(q[i].v); if(x!=y){ f[y]=x; v[q[i].u].push_back(q[i].v); v[q[i].v].push_back(q[i].u); } } } void dfs(int x,int fa){ F[x]=fa,tot[x]=1; for(int u:v[x]){ if(u==fa)continue; dfs(u,x); tot[x]+=tot[u]; } } void Kruskal(){ for(int i=1;i<(n<<1);i++)f[i]=i,sz[i]=1,v[i].clear(); sort(q+1,q+1+m); for(int i=1,cnt=n;i<=m&&cnt<(n<<1)-1;i++){ int x=getf(q[i].u),y=getf(q[i].v); if(x!=y){ ++cnt; f[x]=f[y]=cnt; sz[cnt]=sz[x]+sz[y]; v[cnt].push_back(x); v[cnt].push_back(y); int t1=(F[q[i].u]==q[i].v?tot[q[i].u]:n-tot[q[i].v]); int t2=n-t1; ans[x]+=1ll*t2*sz[y]; ans[y]+=1ll*t1*sz[x]; } } } void dfs2(int x,int fa){ for(int u:v[x])if(u!=fa)ans[u]+=ans[x],dfs2(u,x); } int main(){ scanf("%d%d%d",&n,&m,&Q); for(int i=1;i<=m;i++)scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].w); kruskal(); dfs(1,0); Kruskal(); dfs2((n<<1)-1,0); while(Q--){ int x;scanf("%d",&x); printf("%lld\n",ans[x]); } return 0; }
|