標籤:hdu 貪心 圖論
題目:
給定一棵樹和一些路徑 問 最多能選出多少路徑放在樹上 使得各個路徑間沒有點交叉
思路:
LCA+貪心
對於一條路徑 我們可以將它分成兩部分 即 從u到lca 和 從v到lca 易知lca位於樹上深度最淺的地方 而且如果這個lca被一條路徑覆蓋了 那麼下面的子樹都相當於被覆蓋了
考慮到以x點為上述的lca點 那麼如何選擇經過x的路徑呢 可以想到如果一條路徑能放上去且不和子樹中的路徑衝突那麼才去放它 為什麼呢? 顯然放一條路徑相當於覆蓋一棵樹然後答案加一 那麼如果這條路徑和子樹路徑衝突放它是不值的 因為覆蓋的樹變大了 答案至少要減一再加一
因此得到策略 對於所有路徑求lca 根據lca深度從深到淺安放路徑 如果路徑不和其他路徑衝突則放上
代碼:
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;#define N 100010#define M 20#define mp(x,y) make_pair(x,y)int lca[N][M], dep[N], fa[N];vector<pair<int, int> > ask[N];vector<int> tree[N];int n, m, ans;void dfs(int u, int from) {int i, v;dep[u] = dep[from] + 1;lca[u][0] = from;for (i = 1; i < M; i++)lca[u][i] = lca[lca[u][i - 1]][i - 1];for (i = 0; i < tree[u].size(); i++) {v = tree[u][i];if (v != from)dfs(v, u);}}int getlca(int u, int v) {if (dep[v] > dep[u])swap(u, v);int i, tmp = dep[u] - dep[v];for (i = 19; tmp; i--) {if (tmp >= (1 << i)) {tmp -= (1 << i);u = lca[u][i];}}if (u == v)return u;for (i = 19; i >= 0; i--) {if (lca[u][i] != lca[v][i]) {u = lca[u][i];v = lca[v][i];}}return lca[u][0];}int getf(int x) {if (x != fa[x])fa[x] = getf(fa[x]);return fa[x];}void solve(int u, int from) {int i, v, f;for (i = 0; i < tree[u].size(); i++) {v = tree[u][i];if (v != from)solve(v, u);}for (i = 0; i < ask[u].size(); i++) {f = getf(ask[u][i].first);v = getf(ask[u][i].second);if (f == v && f == u) {ans++;break;}}if (i >= ask[u].size())fa[u] = lca[u][0];}int main() {int i, u, v;while (~scanf("%d%d", &n, &m)) {for (i = 1; i <= n; i++) {ask[i].clear();tree[i].clear();dep[i] = 0;fa[i] = i;}for (i = 1; i < n; i++) {scanf("%d%d", &u, &v);tree[u].push_back(v);tree[v].push_back(u);}dfs(1, 1);for (i = 1; i <= m; i++) {scanf("%d%d", &u, &v);ask[getlca(u, v)].push_back(mp(u,v));}ans = 0;solve(1, 1);printf("%d\n", ans);}return 0;}