AOJ1196 Bridge Removal
問題
方針
木上で最も遠い2点間の距離を直径といいます。 その両端を結ぶパスを通るように移動し、枝分かれがあったらそっちにそれた後また戻ってくるように移動します。 すると、各辺の答えに重みを足す回数は以下のようになります。
- 葉につながる辺 : 1回
- 直径 : 2回
- それ以外の辺 : 3回
直径は Double Sweep と呼ばれる方法で効率的に求めることができます。 実装方法としては葉を足した後削り、残った辺の重みの3倍を足して直径の分だけ引いています。
実装
typedef int Weight;
struct Edge {
int src, dst;
Weight weight;
Edge(int src_, int dst_, Weight weight_) :
src(src_), dst(dst_), weight(weight_) { }
};
bool operator < (const Edge &e, const Edge &f) {
return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;
typedef vector<Weight> Array;
typedef vector<Array> Matrix;
int const inf = 1<<29;
typedef pair<Weight, int> Result;
Result visit(int p, int v, const Matrix & g) {
Result r(0,v);
int n = g.size();
rep(i,n){
if(i!=p){
if(g[v][i]==inf) continue;
Result t = visit(v, i, g);
t.first += g[v][i];
if (r.first < t.first) r = t;
}
}
return r;
}
tuple<int,int,Weight> diameter(const Matrix & g, int start) {
Result r = visit(-1, start, g);
Result t = visit(-1, r.second, g);
return mt(r.second, t.second, t.first); // (r.second, t.second) is farthest pair
}
Matrix mat;
int deg[1000];
int n;
int main(){
while(cin >> n && n){
mat.assign(n,Array(n,inf));
int ans = 0;
rep(i,n) deg[i] = 0;
{
int p[1000], d[1000];
rep(i,n-1) cin >> p[i];
rep(i,n-1) cin >> d[i];
rep(i,n-1){
p[i]--;
mat[i+1][p[i]] = mat[p[i]][i+1] = d[i];
deg[i+1]++;
deg[p[i]]++;
}
}
int t = 0;
rep(i,n){
if(deg[i]!=1) continue;
rep(j,n){
if(mat[i][j]==inf) continue;
t += mat[i][j];
mat[i][j] = mat[j][i] = inf;
}
}
ans += t;
int u = 0;
rep(i,n){
deg[i] = 0;
rep(j,n){
if(mat[i][j]==inf) continue;
deg[i]++;
u += mat[i][j];
}
}
ans += u/2*3;
int start = -1;
rep(i,n){
if(deg[i]==0) continue;
start = i;
break;
}
if(start==-1){
cout << ans << endl;
} else {
int a,b,d;
tie(a,b,d) = diameter(mat,start);
ans -= d;
cout << ans << endl;
}
}
}