1. 程式人生 > >SPOJ Two Paths 樹上不相交路徑乘積最大值

SPOJ Two Paths 樹上不相交路徑乘積最大值

題目連結

這道題其實順著直徑往下想也不是很難

首先關於直徑的證明看一下這裡

我們不難想到這個題目要求的東西應該也和直徑有關

如果其中一條路徑是直徑 那麼另一條就是剩下的路徑中的最大值了

如果一條不是直徑 那它一定是直徑的另一部分和這個點子樹內一條鏈構成的

這個東西證明起來不是很難 因為如果一個點在直徑上

那麼它到直徑兩個端點的距離一定比它剩下的任何一棵子樹內一個點的距離更大

不然的話用這個子樹內那條比直徑長的路徑當直徑肯定會更優

然後就證完了

首先從任意一個點進行dfs 求出直徑 並把直徑上的點存下來

對直徑上每個點dfs一次,找到它子樹內距離它最遠的點

然後對直徑上每個點 從這個點把這棵樹分成兩部分

分別找到被分開的聯通塊的直徑 把最大的兩個乘起來就可以了

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; }