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

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

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

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

#include 
#include 
#include 
#include 
#include 
#include 
#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;
vectorg[N];
vectoradd[N];
vectordes[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;
}

 

你可能感兴趣的:(HDU 5029(树链剖分 + 线段树 好题))