数据中无向图无自环;
50% 的数据N≤2 000 M≤3 000;
80% 的数据N≤50 000 M≤100 000;
100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9
。
这题的关键就在于求Lca,记录路径上的最小与严格次小值.
用f[i][j]表示i的第2^j个儿子(0 表示 不存在)
那么f[i][j]=f[ f[i][ j-1] ][j-1]
dp[i][j]和dp0[i][j]表示点i到f[i][j]的最小和严格次小值(不存在=-1),那么只需特判即可.
int lca(int x,int y,int &nowdp,int &nowdp0) { if (deep[x]<deep[y]) swap(x,y); int t=deep[x]-deep[y]; //差的数量 for (int i=0;t;i++) if (t&bin[i]) //转化为位运算 bin[i]表示2<<i 把t看成2进制 { x=f[x][i]; t-=bin[i]; } int i=Li-1; //Li 表示 最高存到2^(Li-1)个父亲 while (x^y) //x和y不相等时 { while (f[x][i]==f[y][i]&&i) i--; //当i==0时只能向上跳 x=f[x][i];y=f[y][i]; } }程序:
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<functional> using namespace std; #define MAXN (100000+10) #define MAXM (600000+10) #define Li (17) #define INF (2000000000) int edge[MAXM],pre[MAXM],weight[MAXM],next[MAXM],size=0; int addedge(int u,int v,int w) { edge[++size]=v; weight[size]=w; next[size]=pre[u]; pre[u]=size; } int addedge2(int u,int v,int w) { addedge(u,v,w); addedge(v,u,w); } int f[MAXN][Li]={0},dp[MAXN][Li]={0},dp0[MAXN][Li]={0},deep[MAXN],n,m; struct E { int u,v,w; friend bool operator<(E a,E b){return a.w<b.w; } }e[MAXM]; bool b[MAXM],vis[MAXN]; int queue[MAXN],head,tail; void bfs() { memset(vis,0,sizeof(vis)); head=tail=1;queue[1]=1;vis[1]=1;deep[1]=0; while (head<=tail) { int &u=queue[head]; if (u!=1) { for (int i=1;i<17;i++) { if (f[u][i-1]) { f[u][i]=f[f[u][i-1]][i-1]; } if (f[u][i]==0) break; if (f[u][i]) { dp[u][i]=max(dp[u][i-1],dp[f[u][i-1]][i-1]); } if (i==1) { if (dp[u][0]!=dp[f[u][0]][0]) dp0[u][1]=min(dp[u][0],dp[f[u][0]][0]); else dp0[u][1]=-1; } else { dp0[u][i]=max(dp0[u][i-1],dp0[f[u][i-1]][i-1]); if (dp[u][i-1]!=dp[f[u][i-1]][i-1]) dp0[u][i]=max(dp0[u][i],min(dp[u][i-1],dp[f[u][i-1]][i-1])); } } } for (int p=pre[u];p;p=next[p]) { int &v=edge[p]; if (!vis[v]) { queue[++tail]=v; vis[v]=1;deep[v]=deep[u]+1; f[v][0]=u;dp[v][0]=weight[p];dp0[v][0]=-1; } } head++; } } int bin[Li]; void check(int &nowdp,int &nowdp0,int c) { if (c<=nowdp0) return; else if (nowdp0<c&&c<nowdp) nowdp0=c; else if (c==nowdp) return; else if (nowdp<c) {nowdp0=nowdp;nowdp=c;} } int lca(int x,int y,int &nowdp,int &nowdp0) { nowdp=nowdp0=-1; if (deep[x]<deep[y]) swap(x,y); int t=deep[x]-deep[y]; for (int i=0;t;i++) if (t&bin[i]) { check(nowdp,nowdp0,dp[x][i]); check(nowdp,nowdp0,dp0[x][i]); x=f[x][i]; t-=bin[i]; } int i=Li-1; while (x^y) { while (f[x][i]==f[y][i]&&i) i--; check(nowdp,nowdp0,dp[x][i]); check(nowdp,nowdp0,dp0[x][i]); check(nowdp,nowdp0,dp[y][i]); check(nowdp,nowdp0,dp0[y][i]); x=f[x][i];y=f[y][i]; } } int father[MAXN]; long long sum_edge=0; int getfather(int x) { if (father[x]==x) return x; father[x]=getfather(father[x]); return father[x]; } void union2(int x,int y) { father[father[x]]=father[father[y]]; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) father[i]=i; memset(b,0,sizeof(b)); memset(next,0,sizeof(next)); for (int i=0;i<Li;i++) bin[i]=1<<i; for (int i=1;i<=m;i++) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); } sort(e+1,e+1+m); for (int i=1;i<=m;i++) { if (getfather(e[i].u)!=getfather(e[i].v)) {union2(e[i].u,e[i].v);addedge2(e[i].u,e[i].v,e[i].w);sum_edge+=e[i].w; } else b[i]=1; } bfs(); long long mindec=-1; for (int i=1;i<=m;i++) if (b[i]) { int nowdp,nowdp0; lca(e[i].u,e[i].v,nowdp,nowdp0); if (nowdp==e[i].w) nowdp=nowdp0; if (nowdp==-1) continue; if (mindec==-1||mindec>e[i].w-nowdp) mindec=e[i].w-nowdp; } printf("%lld\n",sum_edge+mindec); return 0; }