HDU 4912 - Paths on the tree (LCA 贪心)

本博客探讨了一个关于树形结构的问题,即在给定的树中有多少条路径可以被选择,使得任何两条路径之间都没有公共点。通过深度优先搜索和路径长度计算,实现了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Paths on the tree

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)

Total Submission(s): 600    Accepted Submission(s): 210



Problem Description
bobo has a tree, whose vertices are conveniently labeled by 1,2,…,n.

There are m paths on the tree. bobo would like to pick some paths while any two paths do not share common vertices.

Find the maximum number of paths bobo can pick.
 

Input
The input consists of several tests. For each tests:

The first line contains n,m (1≤n,m≤10 5). Each of the following (n - 1) lines contain 2 integers a i,b i denoting an edge between vertices a i and b i (1≤a i,b i≤n). Each of the following m lines contain 2 integers u i,v i denoting a path between vertices u i and v i (1≤u i,v i≤n).
 

Output
For each tests:

A single integer, the maximum number of paths.
 

Sample Input
  
3 2 1 2 1 3 1 2 1 3 7 3 1 2 1 3 2 4 2 5 3 6 3 7 2 3 4 5 6 7
 

Sample Output
  
1 2
 

Author
Xiaoxu Guo (ftiasch)
 

Source
 

Recommend
We have carefully selected several similar problems for you:   4930  4929  4928  4926  4925 
 


题意:

给一n<=10000个点的树,和m<=10000条路径(用起点和终点表示),问最多可以选择多少条路径,使得所选的任意两条路径之间没有公共点。


思路:

直观的感觉就是选择尽量靠下面的,但是路径之间的高度怎么比较呢

路径的最高点就是起点和终点的LCA,根据这个LCA的按照深度又深到浅选择,不可选择则跳过,可以选择则选择,然后把该路径最高点即前面说的LCA下面的所有子树标记为不可以选择,这样就可以O(1)判断一条路径是否可以选择



#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
#include <cmath>
#include <queue>
#include <set>
#pragma comment(linker, "/STACK:102400000")

using namespace std;

//#define WIN
#ifdef WIN
typedef __int64 LL;
#define iform "%I64d"
#define oform "%I64d\n"
#define oform1 "%I64d"
#else
typedef long long LL;
#define iform "%lld"
#define oform "%lld\n"
#define oform1 "%lld"
#endif

#define S64I(a) scanf(iform, &(a))
#define P64I(a) printf(oform, (a))
#define P64I1(a) printf(oform1, (a))
#define REP(i, n) for(int (i)=0; (i)<n; (i)++)
#define REP1(i, n) for(int (i)=1; (i)<=(n); (i)++)
#define FOR(i, s, t) for(int (i)=(s); (i)<=(t); (i)++)

const int INF = 0x3f3f3f3f;
const double eps = 10e-9;
const double PI = (4.0*atan(1.0));

const int maxn = 100000 + 20;
vector<int> G[maxn];
vector<int> query[maxn];

struct Path {
    int u, v, lca, deep;
};

bool cmp(Path a, Path b) {
    return a.deep > b.deep;
}

Path paths[maxn];
int fa[maxn];
int deep[maxn];
int vis[maxn];

int find(int x) {
    return x==fa[x] ? x : fa[x] = find(fa[x]);
}

void Union(int u, int v) {
    int fu = find(u);
    int fv = find(v);
    fa[fv] = fu;
}

void dfs(int u) {
    fa[u] = u;
    vis[u] = 1;
    for(int i=0; i<G[u].size(); i++) {
        int v = G[u][i];
        if(vis[v]) continue;
        deep[v] = deep[u] + 1;
        dfs(v);
        Union(u, v);
    }
    for(int i=0; i<query[u].size(); i++) {
        int ii = query[u][i];
        int v = paths[ii].u^paths[ii].v^u;
        if(vis[v]) {
            paths[ii].lca = find(v);
            paths[ii].deep = deep[paths[ii].lca];
        }
    }
}

void setc(int u) {
    vis[u] = 1;
    for(int i=0; i<G[u].size(); i++) {
        int v = G[u][i];
        if(vis[v] || deep[v] <= deep[u]) continue;
        setc(v);
    }
}

int main() {
    int n, m;

    while(scanf("%d%d", &n, &m) != EOF) {
        for(int i=0; i<=n; i++) G[i].clear();
        for(int i=0; i<=m; i++) query[i].clear();
        for(int i=1; i<n; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for(int i=0; i<m; i++) {
            scanf("%d%d", &paths[i].u, &paths[i].v);
            query[paths[i].u].push_back(i);
            query[paths[i].v].push_back(i);
        }
        memset(vis, 0, sizeof(vis));
        deep[1] = 1;
        dfs(1);
        sort(paths, paths+m, cmp);
        int ans = 0;
        memset(vis, 0, sizeof(vis));
        for(int i=0; i<m; i++) {
            int u = paths[i].u;
            int v = paths[i].v;
            int lca = paths[i].lca;
            if(vis[u] || vis[v]) continue;
            ans++;
            setc(lca);
        }
        printf("%d\n", ans);
    }

    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值