1. 程式人生 > >51nod 1737 思維+ 樹重心

51nod 1737 思維+ 樹重心

思路: 對於每一條邊,我們如果想要使得他發揮最大價值,其實就是這條邊被用了  min(sonsz[ u ], sonsz[ v ])次,那麼我如果找到一個點使得刪掉這個點之後所有的聯通分支的點的個數都小於等於n/2個點,那麼就可以構造出一種方案使得每條邊被使用min(sonsz[ u ], sonsz[ v ])次。所以就是找樹的重心就可以了。

程式碼:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int ,ll > pil;
const int N =1e5+5;
const int inf =0x3f3f3f3f;

int sz[N];
vector< pil >ve[N];
int n;
ll ans;
int zx;
int mn;

void dfs1(int u,int fa)
{
    sz[u]=0;
    int tmp=0;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i].first;
        if(v==fa) continue;
        dfs1(v,u);
        tmp=max(tmp,sz[v]+1);
        sz[u]+=(sz[v]+1);
    }
    tmp=max(tmp,n-sz[u]-1);
    if(tmp<mn)
    {
        mn=tmp;
        zx=u;
    }
}

void dfs2(int u,int fa,ll dis)
{
    ans+=dis;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i].first;
        ll w=ve[u][i].second;
        if(v==fa) continue;
        dfs2(v,u,dis+w);
    }
}

int main()
{
    int u,v; ll w;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d %d %lld",&u,&v,&w);
        ve[u].push_back(pil(v,w));
        ve[v].push_back(pil(u,w));
    }
    mn=inf;
    dfs1(1,1);
    ans=0;
    //printf("zx %d\n",zx);
    dfs2(zx,zx,0);
    printf("%lld\n",ans);
    return 0;
}

那麼直接對於每條邊進行價值乘也對。

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<int ,ll > pil;
const int N =1e5+5;
int sz[N];
vector< pil >ve[N];
int n;
ll ans;

void dfs(int u,int fa)
{
    sz[u]=1;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i].first;
        if(v==fa) continue;
        dfs(v,u);
        ll w=ve[u][i].second;
        ans+=w*min(sz[v],n-sz[v]);
        sz[u]+=sz[v];
    }
}

int main()
{
    int u,v; ll w;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d %d %lld",&u,&v,&w);
        ve[u].push_back(pil(v,w));
        ve[v].push_back(pil(u,w));
    }
    dfs(1,1);
    printf("%lld\n",ans);

    return 0;
}