HDU 5029(树链剖分 + 线段树 好题)

本文介绍了一道HDU 5029的题目,涉及到树链剖分和线段树的综合运用。题目要求在经过多次路径标记操作后,统计每个节点被哪种标记覆盖的次数最多。解决方法包括树剖和一种特殊形式的线段树,该线段树用于维护每个标记的最大出现次数。通过差分操作更新节点状态,并以扫描线的思想处理所有节点,实现了高效的时间复杂度解决方案。
摘要由CSDN通过智能技术生成

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5029

题意:有一颗树,每次对树上两个点之间的路径用一个值进行标记,m次操作后,问每个点被哪种标记标记的次数最多。

分析:树剖没有什么难度,主要就是线段树,一开始想怎么维护每个点被哪些标记标记过和被标记最多的是哪种标记,但是由于标记种树最大是1e5所以没有什么好的方法。看了大牛的博客才想到建一棵类似于权值线段树。这颗线段树维护当前所有标记中哪种标记次数最多,然后每一次查询做成差分的形式,在起点处对相应标记+1,终点处对相应标记-1,用vector记录下来。依次对1-n个点处理答案,做成了差分的形式,有点类似于扫描线。非常好的题,提供了线段树上非常良好的思路,而且复杂度是非常优秀的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
#define ld d<<1
#define rd d<<1|1
#define lson ld,l,m
#define rson rd,m+1,r
using namespace std;
const int N = 100000 + 5;
int n,m;
vector<int>g[N];
vector<int>add[N];
vector<int>des[N];
int dfn[N],dep[N],fa[N],siz[N],son[N],top[N],rnk[N],tot;
int sum[N<<2];
int maxx[N<<2];
int ans[N];
void dfs1(int u,int f,int d)
{
    dep[u] = d + 1;
    fa[u] = f;
    siz[u] = 1;
    son[u] = -1;
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i];
        if(v == f) continue;
        dfs1(v,u,d+1);
        if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
    return;
}
void dfs2(int u,int t)
{
    dfn[u] = tot;
    rnk[tot++] = u;
    top[u] = t;
    if(son[u] == -1) return;
    dfs2(son[u],t);
    for(int i = 0; i < g[u].size(); i++)
    {
        int v = g[u][i];
        if(v == fa[u] || v == son[u]) continue;
        dfs2(v,v);
    }
    return;
}
int col[N<<2];
void build(int d, int l,int r)
{
    sum[d] = 0;
    maxx[d] = 0;
    if(l ==r)
    {
        maxx[d] = l;
        return;
    }
    int m = (l+r) >>1;
    build(lson);
    build(rson);
    return;
}
void pushup(int d)
{
    if(sum[ld] >= sum[rd]) sum[d] = sum[ld], maxx[d] = maxx[ld];
    else sum[d] = sum[rd], maxx[d] = maxx[rd];
}
void update(int d,int l,int r,int p,int v)
{
    if(l == r)
    {
        sum[d] += v;
        return;
    }
    int m = (l + r) >>1;
    if(p <= m) update(lson,p,v);
    else update(rson,p,v);
    pushup(d);
}
void solve(int x,int y,int c)
{
    int fx = top[x], fy = top[y];
    while(fx != fy)
    {
        if(dep[fx] < dep[fy]) swap(fx,fy) , swap(x,y);
       add[dfn[fx]].push_back(c);
       des[dfn[x] + 1].push_back(c);
        x = fa[fx]; fx = top[x];
    }
    if(dep[x] > dep[y]) swap(x,y);
    add[dfn[x]].push_back(c);
    des[dfn[y] + 1].push_back(c);
    return;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        if(n == 0 && m == 0) break;
        for(int i = 1; i <= n; i++) g[i].clear(),add[i].clear(),des[i].clear();
        tot = 1;
        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);
        }
        dfs1(1,0,0);
        dfs2(1,1);
        for(int i = 0; i < m; i++)
        {
            int x,y,z; scanf("%d%d%d",&x,&y,&z);
            solve(x,y,z);
        }
        build(1,1,100000);
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < add[i].size(); j++)
            {
                update(1,1,100000,add[i][j],1);
            }
            for(int j = 0; j < des[i].size(); j++)
            {
                update(1,1,100000,des[i][j],-1);
            }
            if(sum[1] != 0) ans[rnk[i]] = maxx[1];
            else ans[rnk[i]] = 0;
        }
        for(int i = 1; i <= n; i++) printf("%d\n",ans[i]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值