题目链接
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足: ( v a l u e ( e ) 表 示 边 e 的 权 值 ) ∑ e ∈ E M v a l u e ( e ) < ∑ e ∈ E S v a l u e ( e ) ∑ e ∈ E M v a l u e ( e ) < ∑ e ∈ E S v a l u e ( e ) (value(e)表示边e的权值) \sum_{e \in E_M}value(e)<\sum_{e \in E_S}value(e)∑ e∈EMvalue(e)<∑ e∈E Svalue(e) (value(e)表示边e的权值)∑e∈EMvalue(e)<∑e∈ESvalue(e)∑e∈EMvalue(e)<∑e∈ESvalue(e)
这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
11
数据中无向图无自环; 50% 的数据 N ≤ 2000 M ≤ 3000 N≤2 000 M≤3 000 N≤2000M≤3000;80% 的数据 N ≤ 50000 M ≤ 100000 N≤50 000 M≤100 000 N≤50000M≤100000; 100% 的数据 N ≤ 100000 M ≤ 300000 N≤100 000\quad M≤300 000 N≤100000M≤300000 ,边权值非负且不超过 1 0 9 10^9 109。
首先跑一遍 k r u s k a l kruskal kruskal,标记选中的树边,再枚举每条非树边更新答案。
设 F [ x , k ] F[x,k] F[x,k]表示 x x x的 2 k 2^k 2k辈祖先, G [ x , k , 0 ] G[x,k,0] G[x,k,0]与 G [ x , k , 1 ] G[x,k,1] G[x,k,1]分别表示从 x x x到 F [ x , k ] F[x,k] F[x,k]的路径上的最大边权和严格次大边权。于是 ∀ k ∈ [ 1 , l o g N ] ∀k∈[1,log N] ∀k∈[1,logN]有:
G [ x , k , 0 ] = m a x ( G [ x , k − 1 , 0 ] , G [ F [ x , k − 1 , k − 1 , 0 ] ) G[x,k,0]=max(G[x,k-1,0],G[F[x,k-1,k-1,0]) G[x,k,0]=max(G[x,k−1,0],G[F[x,k−1,k−1,0])
G [ x , k , 1 ] = { m a x ( G [ x , k − 1 , 1 ] , G [ F [ x , k − 1 ] , k − 1 , 1 ] ) G [ x , k − 1 , 0 ] = G [ F [ x , k − 1 ] , k − 1 , 0 ] m a x ( G [ x , k − 1 , 0 ] , G [ F [ x , k − 1 ] , k − 1 , 1 ] ) G [ x , k − 1 , 0 ] < G [ F [ x , k − 1 ] , k − 1 , 0 ] m a x ( G [ x , k − 1 , 1 ] , G [ F [ x , k − 1 ] , k − 1 , 0 ] ) G [ x , k − 1 , 0 ] > G [ F [ x , k − 1 ] , k − 1 , 0 ] } G[x,k,1]=\begin{Bmatrix} max(G[x,k-1,1],G[F[x,k-1],k-1,1])&G[x,k-1,0]=G[F[x,k-1],k-1,0] \\ max(G[x,k-1,0],G[F[x,k-1],k-1,1])&G[x,k-1,0]<G[F[x,k-1],k-1,0]\\max(G[x,k-1,1],G[F[x,k-1],k-1,0])&G[x,k-1,0]>G[F[x,k-1],k-1,0]\end{Bmatrix}\quad G[x,k,1]=⎩⎨⎧max(G[x,k−1,1],G[F[x,k−1],k−1,1])max(G[x,k−1,0],G[F[x,k−1],k−1,1])max(G[x,k−1,1],G[F[x,k−1],k−1,0])G[x,k−1,0]=G[F[x,k−1],k−1,0]G[x,k−1,0]<G[F[x,k−1],k−1,0]G[x,k−1,0]>G[F[x,k−1],k−1,0]⎭⎬⎫
整个算法时间复杂度 O ( M l o g N ) O(MlogN) O(MlogN)
(蒟蒻第一次打这么优美的公式好激动惹)(当然公式本身还是抄的书上的)
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int M=3e5+10;
int n,m,father[N],dep[N],hd[N],tot,cnt,fa[N][20];
ll ans,g[N][20][2],Div=0x7fffffff;
struct Edge{
int u,v,w,cho;
bool operator <(const Edge&rhs)const{
return wdep[v])swap(u,v);
for(int i=19;i>=0;i--)
if(dep[fa[v][i]]>=dep[u])v=fa[v][i];
if(u==v)return u;
for(int i=19;i>=0;i--)
if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void calc(int u,int w,int an)
{
ll mx1=0,mx2=0;
int d=dep[u]-dep[an];
for(int i=0;i<20;i++)
if(d&(1<mx1)mx2=mx1,mx1=g[u][i][0];
mx2=max(mx2,g[u][i][1]);
u=fa[u][i];
}
if(mx1!=w)Div=min(Div,w-mx1);
else Div=min(Div,w-mx2);
}
int main()
{
//freopen("in.txt","r",stdin);
memset(hd,-1,sizeof(hd));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),e[i].cho=0;
kruskal();
dfs(1,0);
for(int i=1;i<=m;i++)if(!e[i].cho)calc(e[i].u,e[i].w,lca(e[i].u,e[i].v)),calc(e[i].v,e[i].w,lca(e[i].u,e[i].v));
printf("%lld\n",ans+Div);
return 0;
}
树上倍增法求严格次小生成树