HDU 6203 ping ping ping LCA + dfs序 + 树状数组(区间更新单点查询)

传送门:HDU 6203

题意:给出n+1 个点的树,其中有若干个点无法通过,导致 p 组顶点对(u, v)之间无法连通。问无法通行的点最少有多少个。

思路:对p组(u,v)顶点对求LCA,并按LCA的深度排序,从深度最大的开始处理,然后在dfs序上进行标记,若当前顶点对之间已经不可达,则直接跳过,否则将其LCA在dfs序上标记出来。

具体标记及判断方法详见:点击打开链接

树状数组区间更新单点查询就很类似于imos和思想,也很常用。

代码:

#include<bits/stdc++.h>  
using namespace std;  
#define MAXN 10050
#define MAXM 10050
#define inf 0x3f3f3f3f  
typedef pair<int,int>P;  
int dep[MAXN], pre_ord[MAXN], post_ord[MAXN];
int f[20][MAXN], pre[MAXN];  
int cnt, tid;  
struct node  
{  
    int v, next;  
    node(int _v = 0, int _next = 0) : v(_v), next(_next) {}  
}mp[MAXM * 2]; 
void init()  
{  
    cnt = tid = 0;
    memset(pre, -1, sizeof(pre));
}
void add(int u, int v)  
{  
    mp[cnt] = node(v, pre[u]), pre[u] = cnt++;  
    mp[cnt] = node(u, pre[v]), pre[v] = cnt++;
}  
void dfs(int u, int fa)  
{  
    dep[u] = dep[fa] + 1;
    pre_ord[u] = ++tid;
    for(int i = pre[u]; ~i; i = mp[i].next)  
    {  
        int v = mp[i].v;  
        if(v == fa) continue;  
        dfs(v, u);  
        f[0][v] = u;  
    }
    post_ord[u] = ++tid;
}  
  
int lca_init(int n)  
{  
    dep[1] = 1;  
    dfs(1, 0);  
    int k = 0,t = 1;  
    while(t <= n)t <<= 1,k++;  
    for(int i=0;i+1<k;i++)  
    {  
        for(int j=1;j<=n;j++)  
        {  
           f[i+1][j] = f[i][f[i][j]];  
        }  
    }  
    return k;  
}
int lca(int u, int v, int MAX)  
{  
    if(dep[u] < dep[v]) swap(u, v);  
    int k = dep[u] - dep[v];  
    for(int i = 0;i < MAX; i++)  
    {  
        if((k >> i) & 1)  
        u = f[i][u];  
    }  
    if(u == v)return u;  
    for(int i = MAX - 1;i >= 0; i--)  
    while(f[i][u] != f[i][v])  
    {  
        u = f[i][u];  
        v = f[i][v];  
    }  
    return f[0][u];  
}
struct query{
    int u, v, lca;
    query(int _u = 0, int _v = 0, int _lca = 0) : u(_u), v(_v), lca(_lca) {}
    bool operator < (query x) const
    {
        return dep[lca] > dep[x.lca];
    }
}Q[MAXN * 5];
int bit[MAXN << 1];
void update(int i, int x)
{
    while(i <= tid)
    {
        bit[i] += x;
        i += i & -i;
    }
}
int sum(int i)
{
    int res = 0;
    while(i)
    {
        res += bit[i];
        i -= i & -i;
    }
    return res;
}
int main()
{
    int T, n, u, v, q;
    while(~scanf("%d", &n))
    {
        init();
        memset(bit, 0, sizeof(bit));
        for(int i = 1; i <= n; i++)
        {
            scanf("%d %d", &u, &v);
            add(++u, ++v);
        }
        n++;
        int up = lca_init(n);
        scanf("%d", &q);
        for(int i = 0; i < q; i++)
        {
            scanf("%d %d", &u, &v);
            u++,v++; 
            Q[i] = query(u, v, lca(u, v, up));
        }
        sort(Q, Q + q);
        int is, ans = 0;
        for(int i = 0; i < q; i++)
        {
            is = sum(pre_ord[Q[i].u]) + sum(pre_ord[Q[i].v]);
            if(!is)
            {
                ans++;
                update(pre_ord[Q[i].lca], 1);
                update(post_ord[Q[i].lca] + 1, -1);
            }
        }
        cout << ans << endl;
    }
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值