CF773D Perishable Roads

Statement

Problem Linkspa

給定一個 \(n\) 點的無向徹底圖,有邊權。對於每一個 \(i\in [1,n]\) ,求以 \(i\) 爲根節點的生成樹中,最小的 \(\sum d(x)\) 是多少。code

定義 \(d(x)\)\(x\) 到根節點全部邊中權值最小的一條blog

Solution

不愧是 tourist (這場比賽裏)最喜歡的題啊……get

因爲代價是取 \(\min\) 的,考慮找最小的邊權。若是這條邊已經被連到了根,那麼剩下的點直接連向這個點便可(徹底圖)。io

設最小的邊爲 \(edmn\) ,易知最優解(或之一)的上面部分必定是一條根到 \(edmn\) 兩個端點之一的鏈。class

因爲這個東西是個徹底圖,並且代價是取 \(\min\) 的,因此其實鏈下面的部分是樹是鏈徹底沒有關係(由於代價已經被 \(edmn\) 給肯定了),形態不重要。下面直接將最優解所選擇的邊當成鏈來處理。dva

那麼把全部邊權減去 \(edmn\) ,最後再加上 \((n-1)\times edmn\) ,不影響答案。基礎

如今設這個最優解的路徑爲 \(w_1,\dots,w_{n-1}\) .令 \(k\) 爲路徑上標號最小的、邊權爲 0 的邊( 也就是最小的 \(k\) 知足 \(w_k=0\)im

有結論: 對於全部的 \(i\leq k-3\) ,有 \(w_i>w_{i+1}\)db

Proof

(看了半天才懂……)

假設如今有這樣一條路徑,\(M\) 是根節點,\(G\) 是令 \(w_k=0\) 的節點。

Graph1

此處 \(w_{CD}\) 不知足上面的性質。那麼能夠將其替換以下:

graph2

\(x\) 是新的邊權。設原來 \(M\sim C\) 的最小值爲 \(t\) ,那麼減小貢獻爲:

\[\delta =calc(D)+calc(E)=\min(w_{CD},t)+\min(w_{DE},w_{CD},t)\\ \because w_{CD}>t,w_{DE}<w_{CD}\\ \therefore \min{(w_{CD},t)}=t,\min(w_{DE},w_{CD},t)=\min(w_{DE},t)\\ \delta=t+\min(w_{DE},t) \]

增長的貢獻是:

\[\Delta =calc(F')-calc(F)=\min(w_{CF},t)-w_{DE}<t<t+\min(w_{DE},t) \]

那麼就有:

\[\delta>\Delta \]

因此這樣替換事後,貢獻必定會減小,就不符合最優解的前提了。

Q.E.D.

有了這個結論事情就簡單多了。除了最後兩條邊,其餘邊的貢獻(因爲遞增)就是邊權,能夠建一個超級源點 \(S\) ,對於全部 0 邊的端點連 0 邊權的邊,(固然要加上原圖中的全部邊)直接跑最短路。

如今來討論 \(k-2\leq i\leq k-1\) 的狀況。

  • \(w_{k-2}>w_{k-1}\) 。那麼狀況就等同於整條路徑符合上面的結論,直接按原先的邊權跑最短路便可,無需特殊處理。
  • \(w_{k-2}\leq w_{k-1}\) 。設當前的路徑是這樣的:A---(k-2)----B----(k-1)----C---0---- ,那麼顯然,這樣的路徑中,咱們先走到 \(A\) ,而後經過兩條路徑走到 0 邊的一個端點 \(C\) ,(根據前面的結論,\(w_i\) 顯然都比 \(w_{k-2}\) 大,因此不用考慮)代價是 \(2\times w_{k-2}\) .那麼就在以前的基礎上再加上一條 \(S,i\) 之間,\(\min\{dis[i][j]\times 2\}\) 的邊便可(注意,須要知足 \(i\neq j\)\(i,j\) 均不是 0 邊端點,至關於在枚舉 \(w_{k-2}\) )。

那麼這樣就作完了。

妙啊。不愧是 tourist

順便一說,Dijkstra 就直接樸素的好了,反正以前的操做已經 \(\mathcal{O}(n^2)\)

Code

//Author: RingweEH
const ll inf=1e16;
const int N=2010;
struct edge
{
    int to,nxt; ll val;
}e[N*N<<1];
int tot=0,head[N],n,S;
bool tag[N],vis[N];
ll mp[N][N],dis[N],mn[N];

void add( int u,int v,ll w )
{
    e[++tot].to=v; e[tot].val=w; e[tot].nxt=head[u]; head[u]=tot;
}

int main()
{
    n=read(); S=n+1; ll edmn=inf;
    for ( int i=1; i<n; i++ )
        for ( int j=i+1; j<=n; j++ )
            mp[j][i]=mp[i][j]=read(),edmn=min( edmn,mp[i][j] );
    for ( int i=1; i<=n; i++ )
        for ( int j=1; j<=n; j++ )
            if ( i^j )
            {
                mp[i][j]-=edmn; add( i,j,mp[i][j] ); 
                if ( !mp[i][j] ) tag[i]=1,add( S,i,0 );
            }
    memset( mn,0x7f,sizeof(mn) );
    for ( int i=1; i<=n; i++ )
        for ( int j=1; j<=n; j++ )
            if ( (!tag[i]) && (!tag[j]) && (i^j) ) mn[i]=min( mn[i],mp[i][j]*2ll );
    for ( int i=1; i<=n; i++ )
        add( S,i,mn[i] );
    memset( dis,0x3f,sizeof(dis) ); dis[S]=0;
    for ( int j=1; j<=n; j++ )
    {
        ll mnval=inf; int mnpos=0;
        for ( int i=1; i<=n+1; i++ )
            if ( !vis[i] && dis[i]<mnval ) mnval=dis[i],mnpos=i;
        vis[mnpos]=1;
        for ( int i=head[mnpos]; i; i=e[i].nxt )
            dis[e[i].to]=min( dis[e[i].to],dis[mnpos]+e[i].val );
    }

    ll addval=(n-1)*edmn;
    for ( int i=1; i<=n; i++ )
        printf( "%lld\n",dis[i]+addval );
    
    return 0;
}
相關文章
相關標籤/搜索