hdu5452(lca + 树上的操作)

题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5452

解法:首先思路肯定是要枚举树上删掉的一条边,然后就转化为求树上分开的两块之间连的其它边的条数。我们看非树边(u,v),如果删除的边在lca(u,v)和u,v之间,那么这条边就会起到贡献,为1,如果删除的边在u,v的子树下,则没有起到贡献,同理如果删除的边在lca(u,v)上面的话,也不会起到作用,那么我们就可以给点打上tag,lca(u,v)处减2,u与v处加1,那么可以统计子树上点的val就可以知道其它边的条数(可以手动验证)。那么打完tag写个深搜统计一下即可。

AC代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

int T;

const int N = 20000 + 5;

int n,m;
const int M = 200000 + 5;

int u[M],v[M];

vector<int> G[N];
int root;

int par[20][N];
int depth[N];

void dfs(int v,int p,int d){
    par[0][v] = p;
    depth[v] = d;
    for(int i = 0;i < (int) G[v].size();i++){
        int u = G[v][i];
        if(u != p) dfs(u,v,d + 1);
    }
}

void init(int V){
    dfs(root,-1,0);
    for(int k = 0;k + 1 < 20;k++){
        for(int v = 0;v < V;v++){
            if(par[k][v] < 0) par[k + 1][v] = -1;
            else par[k + 1][v] = par[k][par[k][v]];
        }
    }
}
int lca(int u,int v){
    if(depth[u] > depth[v]) swap(u,v);
    for(int k = 0;k < 20;k++){
        if((depth[v] - depth[u]) >> k & 1){
            v = par[k][v];
        }
    }
    if(u == v) return u;
    for(int k = 19;k >= 0;k--){
        if(par[k][u] != par[k][v]){
            u = par[k][u];
            v = par[k][v];
        }
    }
    return par[0][u];
}

int val[N];

int sum[N];

void solve(int u,int p){
    sum[u] = val[u];
    for(int i = 0;i < (int) G[u].size();i++){
        int v = G[u][i];
        if(v == p) continue;
        solve(v,u);
        sum[u] += sum[v];
    }
}

int main(){
    cin >> T;
    int cas = 0;
    while(T--){
        printf("Case #%d: ",++cas);
        memset(val,0,sizeof(val));
        memset(sum,0,sizeof(sum));
        cin >> n >> m;
        for(int i = 0;i < n;i++) G[i].clear();

        for(int i = 1;i <= m;i++){
            scanf("%d%d",u + i,v + i);

        }
        root = 0;
        for(int i = 1;i < n;i++){
            G[u[i] - 1].push_back(v[i] - 1);
            G[v[i] - 1].push_back(u[i] - 1);
        }
        init(n);
        for(int i = n;i <= m;i++){
            int fa = lca(u[i] - 1,v[i] - 1);
            val[fa] -= 2;
            val[u[i] - 1]++;
            val[v[i] - 1]++;
        }
        solve(root,-1);
        int ans = 1e9 + 7;
        for(int i = 0;i < n;i++){
            if(i == root) continue;
            else ans = min(ans,sum[i]);
        }
        cout << ans + 1 << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值