BZOJ2521: 最小生成樹 題解

Description

Secsa最近對最小生成樹問題特別感興趣。他已經知道如果要去求出一個n個點、m條邊的無向圖的最小生成樹有一個Krustal算法和另一個Prim的算法。另外,他還知道,某一個圖可能有多種不同的最小生成樹。例如,下面圖 3中所示的都是圖 2中的無向圖的最小生成樹:(圖略)

當然啦,這些都不是今天需要你解決的問題。Secsa想知道對於某一條無向圖中的邊AB,至少需要多少代價可以保證AB邊在這個無向圖的最小生成樹中。爲了使得AB邊一定在最小生成樹中,你可以對這個無向圖進行操作,一次單獨的操作是指:先選擇一條圖中的邊 P1P2,再把圖中除了這條邊以外的邊,每一條的權值都減少1。如圖 4所示就是一次這樣的操作:(圖略)

Input

輸入文件的第一行有3個正整數n、m、Lab分別表示無向圖中的點數、邊數、必須要在最小生成樹中出現的AB邊的標號。
接下來m行依次描述標號爲1,2,3…m的無向邊,每行描述一條邊。每個描述包含3個整數x、y、d,表示這條邊連接着標號爲x、y的點,且這條邊的權值爲d。
輸入文件保證1<=x,y<=N,x不等於y,且輸入數據保證這個無向圖一定是一個連通圖。

Output

輸出文件只有一行,這行只有一個整數,即,使得標號爲Lab邊一定出現最小生成樹中的最少操作次數。

Sample Input

4 6 1
1 2 2
1 3 2
1 4 3
2 3 2
2 4 4
3 4 5

Sample Output

1

HINT

第1個樣例就是問題描述中的例子。
1<=n<=500,1<=M<=800,1<=D<10^6


首先除了自己其他的邊-1相當於給自己+1
一件顯然的事情是我們肯定不會給指定邊做+1操作
我們考慮什麼時候指定邊會不在最小生成樹中,我們考慮kruskal的過程,是把邊從小到大加入樹中的,如果加到(a,b)的時候,a,b在並查集中是連通的,那麼(a,b)就不會被加入,也就是說,如果存在一條路徑,上面的每一條邊權值都不大於w(a,b),那麼(a,b)就可能會不被加入,所以對於每一條可能的從a到b的路徑,都要保證至少有1條邊權值大於w(a,b)
我們考慮把所有權值不大於w(a,b)的邊w(u,v)加進一個新圖中((a,b)不算),權值爲w(a,b)+1-w(u,v),這樣只要求這個圖的最小割就好了
最小割的源匯點肯定是a和b,考慮到這個圖原本是無向的,所以S,T交換肯定對答案沒有影響,所以剛開始可以隨意指定
但是該怎麼給無向圖中的邊定向呢?如果拆成兩條有向邊的話,一條無向邊對應的兩條有向邊如果同時被修改的話,答案是不是會算重呢
事實上,我們確實只要把無向邊拆成兩條有向邊連起來就行了,因爲我們可以證明,在最小割中,這兩條邊不會被同時割掉
證明如下:考慮反證法,我們考慮有向邊uv和vu,如果它們被同時割掉的話,說明原圖中存在兩條路徑S...uv...TS...vu...T ,除了uv和vu其他的邊都在殘餘網絡中,然後我們發現如果這兩條邊被同時割掉的話,路徑1的前半段和路徑2的後半段可以組成一條新的路徑,這樣的話這個圖ST仍是連通的,矛盾

#include <bits/stdc++.h>
using namespace std;

#define LL long long
#define LB long double
#define ull unsigned long long
#define x first
#define y second
#define pb push_back
#define pf push_front
#define mp make_pair
#define Pair pair<int,int>
#define pLL pair<LL,LL>
#define pii pair<double,double>
#define LOWBIT(x) x & (-x)

const int INF=2e9;
const LL LINF=2e16;
const int magic=348;
const int MOD=998244353;
const double eps=1e-10;
const double pi=acos(-1);

inline int getint()
{
    bool f;char ch;int res;
    while (!isdigit(ch=getchar()) && ch!='-') {}
    if (ch=='-') f=false,res=0; else f=true,res=ch-'0';
    while (isdigit(ch=getchar())) res=res*10+ch-'0';
    return f?res:-res;
}

const int MAXN=1e5;

int n,m,ind;
struct Edge
{
    int x,y,c;
    inline void input() {x=getint();y=getint();c=getint();}
}edge[MAXN+48];

int head[MAXN+48],cur[MAXN+48],to[MAXN+48],nxt[MAXN+48],f[MAXN+48],tot=1;
inline void addedge(int s,int t,int cap) {to[++tot]=t;nxt[tot]=head[s];head[s]=tot;f[tot]=cap;}
int S,T;

int depth[MAXN+48],q[MAXN+48],Head,Tail;
inline bool bfs()
{
    int i,x,y;
    for (i=1;i<=n;i++) depth[i]=-1;
    depth[S]=0;q[1]=S;Head=Tail=1;
    while (Head<=Tail)
    {
        x=q[Head++];
        for (i=head[x];i;i=nxt[i])
        {
            y=to[i];
            if (depth[y]==-1 && f[i])
            {
                depth[y]=depth[x]+1;
                q[++Tail]=y;
            }
        }
    }
    if (depth[T]==-1) return false; else return true;
}

inline int dfs(int x,int maxf)
{
    if (x==T) return maxf;
    int y,minf,now,ans=0;
    for (int &i=cur[x];i;i=nxt[i])
    {
        y=to[i];
        if (depth[y]==depth[x]+1 && f[i])
        {
            minf=min(maxf-ans,f[i]);
            now=dfs(y,minf);
            f[i]-=now;f[i^1]+=now;ans+=now;
        }
    }
    if (ans==0) depth[x]=0;
    return ans;
}

int main ()
{
    n=getint();m=getint();ind=getint();int i;
    for (i=1;i<=m;i++) edge[i].input();
    for (i=1;i<=m;i++)
    {
        if (i==ind) continue;
        if (edge[i].c<=edge[ind].c)
        {
            addedge(edge[i].x,edge[i].y,edge[ind].c+1-edge[i].c);
            addedge(edge[i].y,edge[i].x,edge[ind].c+1-edge[i].c);
        }
    }
    S=edge[ind].x;T=edge[ind].y;
    int ans=0;
    while (bfs())
    {
        for (i=1;i<=n;i++) cur[i]=head[i];
        ans+=dfs(S,2e9);
    }
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章