我終於搞懂了!!!
題目大意
求嚴格次小生成樹。
數據中無向圖無自環;
50%的數據N≤2000 M≤3000;
80%的數據N≤50000 M≤100000;
100%的數據N≤100000 M≤300000,邊權值非負且不超過10^9。
題目分析
牆裂推薦這個blog!!!
思路看懂了嗎???
然後直接講代碼???
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
bool v[300010];
int n,m,len=0,c[30],deep[100010];
struct nod1{int x,y,z;}d[300010];
struct nod2{int y,gg,c;}b[200010];
int max1[100010][30],max2[100010][30];
int fa[100010],a[100010],f[100010][30];
int cmp(nod1 x,nod1 y) { return x.z<y.z; }
int find(int x)//並並查集
{
if(fa[x]!=x) return fa[x]=find(fa[x]);
else return fa[x];
}
void ins(int x,int y,int c)
{
len++; b[len].y=y; b[len].c=c;
b[len].gg=a[x]; a[x]=len;
}
void dfs(int x,int fa)
{
f[x][0]=fa; deep[x]=deep[fa]+1;
for(int i=1;i<=21;i++)
{
if(c[i]>deep[x]) break;
//我往上跳的高度不能高於我的深度
f[x][i]=f[f[x][i-1]][i-1];
//倍增求LCA,不懂就畫圖畫圖
}
for(int i=a[x];i;i=b[i].gg)
{
int y=b[i].y;
if(y!=fa)
{
max1[y][0]=b[i].c;//y到x的距離
dfs(y,x);
}
}
}
int lca(int x,int y)//找最近公共祖先
{
if(deep[x]>deep[y]) swap(x,y);
for(int i=21;i>=0;i--)//先跳到同一深度
{
if(deep[x]==deep[y]) break;
if(deep[x]<=deep[y]-c[i]) y=f[y][i];
//y跳到的深度必須大於等於x的深度
}
if(x==y) return x;
for(int i=21;i>=0;i--)
{
if(deep[x]<=c[i]||f[x][i]==f[y][i]) continue;
//往上跳的高度不能高於我的深度並且x,y跳到的位置不能相同
//這個不懂要先去學習 倍增求LCA 啊
x=f[x][i]; y=f[y][i];
}
return f[x][0];
}
int getmax(int x,int y,int ma)
{
int kk=0;
for(int i=21;i>=0;i--)
{
if(x==y) break;
if(deep[f[x][i]]>=deep[y])
{
if(max1[x][i]!=ma) kk=max(kk,max1[x][i]);
//如果max1==ma說明刪去的邊和加上的邊權值相同,顯然不符合題意
else kk=max(kk,max2[x][i]);//那麼ma等於次長
x=f[x][i];
}
}
return kk;
}
int main()
{
memset(v,false,sizeof(v));
scanf("%d%d",&n,&m); c[0]=1;
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=21;i++) c[i]=c[i-1]*2;//2的i次方
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&d[i].x,&d[i].y,&d[i].z);
}
sort(d+1,d+1+m,cmp);
int t=0; long long sum=0;
for(int i=1;i<=m;i++)
{
int fx=find(d[i].x),fy=find(d[i].y);
if(fx!=fy)
{
t++; sum+=d[i].z;
fa[fx]=fy; v[i]=true;
ins(d[i].x,d[i].y,d[i].z);
ins(d[i].y,d[i].x,d[i].z);
}
if(t==n-1) break;
}
//最小生成樹
//printf("sum==%d\n",sum);
deep[0]=0; dfs(1,0);
//找最長邊(max1)和次長邊(max2)
for(int i=1;i<=21;i++)
{
for(int x=1;x<=n;x++)
{
//找最長邊和次長邊
max1[x][i]=max(max1[x][i-1],max1[f[x][i-1]][i-1]);
max2[x][i]=max(max2[x][i-1],max2[f[x][i-1]][i-1]);
//如果你沒有搞清楚他怎麼跳,爲什麼就得出上面的式子
//建議你畫個圖手動模擬一下
int aa=max1[x][i-1],bb=max1[f[x][i-1]][i-1];
if(aa>bb) max2[x][i]=max(max2[x][i],bb);
else if(aa<bb) max2[x][i]=max(max2[x][i],aa);
}
}
long long ans=1e18;
for(int i=1;i<=m;i++)
{
if(!v[i])
{
int lcaa=lca(d[i].x,d[i].y);
int aa=getmax(d[i].x,lcaa,d[i].z);
int bb=getmax(d[i].y,lcaa,d[i].z);
//ma==x到y途中不等於刪去邊的最長邊
//ans=最小生成樹和-ma+非樹邊邊權
ans=min(ans,sum+d[i].z-max(aa,bb));
}
}
printf("%lld",ans);
return 0;
}