Loading

题解-CF101D Castle

题面

CF101D Castle

给一棵 \(n\) 个节点的带权树,求一种遍历方案,从 \(1\) 出发,每条边走两次,走过所有点,第一次经过每个节点的平均时间最小。输出这个平均时间。

数据范围:\(2\le n\le 10^5\)


题解

可以对于每棵子树单独计算。

虽然子树内走的顺序是不固定的,但是要消耗的总时间是固定的。

\(f_u\) 表示 \(u\) 这棵子树内的第一次经过每个节点的 时间和\(w_{u,v}\)\(u,v\) 两点间的边权,\(sz_u\)\(u\) 的子树大小,\(szt_u\) 是走遍 \(u\) 这棵子树的所需时间。

由于每棵子树遍历一次而且有顺序,记 \(v_i\)\(u\)\(i\) 个遍历的子树。

\[f_u=\sum_{i} [f_{v_i}+sz_{v_i}w_{u,v_i}+(szt_{v_i}+2w_{u,v})\sum_{j>i} sz_{v_j}] \]

\(\sum_{i} (f_{v_i}+sz_{v_i}w_{u,v_i})\) 是顺序不同时固定的,\(\sum_{i} (szt_{v_i}+2w_{u,v})\sum_{j>i} sz_{v_j}\) 是会变的,所以这题的难点在于找到它的最小值。

蒟蒻想了好久,这个式子很眼熟,但是怎么化都貌似不能贪心,于是请教了旁边的保队长。

很明显,如果对于 \(i\ge 3\)\(v_i\) 都已经确定,那么 \(p\)\(q\) 分别作为 \(v_1,v_2\) 的贡献是

\[v_1=p,v_2=q:(szt_p+2w_{u,p})(sz_{q}+\sum_{j\ge 3} sz_{v_j})+(szt_p+2w_{u,p})\sum_{j\ge 3}sz_{v_j}+\cdots \]

\[v_1=q,v_2=p:(szt_q+2w_{u,q})(sz_{p}+\sum_{j\ge 3} sz_{v_j})+(szt_q+2w_{u,q})\sum_{j\ge 3}sz_{v_j}+\cdots \]

其实差别就在于 \((szt_p+2w_{u,p})sz_{q}\)\((szt_q+2w_{u,q})sz_{p}\),而且手玩一下可以发现,对于任意两个 \(v\) 顺序都是类似的。

所以把所有 \(v\in son_u\) 丢进一个 vector,然后对于 \(p\)\(q\),按上面的差别排序即可。


代码

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

//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair((a),(b))
#define x first
#define y second
#define bg begin()
#define ed end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i,a,b) for(int i=(a),i##E=(b);i<i##E;i++)
#define L(i,a,b) for(int i=(b)-1,i##E=(a)-1;i>i##E;i--)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f;

//Data
const int N=1e5;
int n;

//Tree
vector<int> e[N],to,we;
void adde(int u,int v,int w){
    e[u].pb(sz(to)),to.pb(v),we.pb(w);
    e[v].pb(sz(to)),to.pb(u),we.pb(w);
}
int sz[N],szt[N];
ll dfs(int u,int fa){
    ll res=0; sz[u]=1,szt[u]=0;
    vector<int> cho;
    for(int v:e[u])if(to[v]^fa){
        res+=dfs(to[v],u);
        res+=1ll*we[v]*sz[to[v]];
        szt[to[v]]+=we[v]*2;
        sz[u]+=sz[to[v]],szt[u]+=szt[to[v]];
        cho.pb(to[v]);
    }
    sort(cho.bg,cho.ed,[&](int p,int q){
        return 1ll*szt[p]*sz[q]<1ll*szt[q]*sz[p];
    });
    int tot=sz[u]-1;
    for(int v:cho) tot-=sz[v],res+=1ll*szt[v]*tot;
    return res;
}

//Main
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cout.precision(12);
    cin>>n;
    R(i,1,n){int u,v,w; cin>>u>>v>>w,--u,--v,adde(u,v,w);}
    // cout<<dfs(0,-1)<<'\n';
    cout<<fixed<<1.*dfs(0,-1)/(n-1)<<'\n';
    return 0;
}

祝大家学习愉快!

posted @ 2020-10-21 13:21  George1123  阅读(124)  评论(0编辑  收藏  举报