SPOJ Two Paths 樹上不相交路徑乘積最大值
阿新 • • 發佈:2018-12-09
這道題其實順著直徑往下想也不是很難
首先關於直徑的證明看一下這裡
我們不難想到這個題目要求的東西應該也和直徑有關
如果其中一條路徑是直徑 那麼另一條就是剩下的路徑中的最大值了
如果一條不是直徑 那它一定是直徑的另一部分和這個點子樹內一條鏈構成的
這個東西證明起來不是很難 因為如果一個點在直徑上
那麼它到直徑兩個端點的距離一定比它剩下的任何一棵子樹內一個點的距離更大
不然的話用這個子樹內那條比直徑長的路徑當直徑肯定會更優
然後就證完了
首先從任意一個點進行 求出直徑 並把直徑上的點存下來
對直徑上每個點一次,找到它子樹內距離它最遠的點
然後對直徑上每個點 從這個點把這棵樹分成兩部分
分別找到被分開的聯通塊的直徑 把最大的兩個乘起來就可以了
Codes
#include<bits/stdc++.h>
#define pb push_back
#define ll long long
using namespace std;
const int N = 1e5 + 10;
int n, dep[N], fa[N], leave;
int len[N], d[N], ind[N], cnt;
int L[N], R[N];
vector<int> G[N]; ll ans;
void dfs1(int x, int dad) {
dep[x] = dep[dad] + 1 , fa[x] = dad;
for(auto v : G[x]) if(v != dad) dfs1(v, x);
if(dep[x] > dep[leave]) leave = x;
}
void dfs2(int x, int dad) {
for(auto v : G[x])
if(v != dad && !ind[v]) {
dfs2(v, x);
len[x] = max(len[x], len[v] + 1);
}
}
int main() {
int x, y, tmp;
scanf("%d", &n);
for(int i = 1; i < n; ++ i) {
scanf("%d%d", &x, &y);
G[x].pb(y), G[y].pb(x);
}
dfs1(1, 0), dfs1(leave, 0);
while(leave) {
d[++ cnt] = leave; ind[leave] = true; leave = fa[leave];
}
for(int i = 1; i <= cnt; ++ i) {
dfs2(d[i], 0); ans = max(ans, 1ll * (len[d[i]] - 1) * (cnt - 1));
}
tmp = 0;
for(int i = 1; i <= cnt; ++ i)
L[i] = tmp = max(tmp, i - 2 + len[d[i - 1]]);
tmp = 0;
for(int i = cnt; i >= 1; -- i)
R[i] = tmp = max(tmp, cnt - i + len[d[i + 1]] - 1);
for(int i = 1; i <= n; ++ i)
ans = max(ans, 1ll * L[i] * R[i - 1]);
cout << ans << endl;
return 0;
}