洛谷 P4180 【模板】嚴格次小生成樹[BJWC2010]

我終於搞懂了!!!

題目

題目大意

求嚴格次小生成樹。

數據中無向圖無自環;

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章