HDU 5029 树链剖分+权值线段树

题目链接


题意:
给定一颗n个结点的树,进行m次染色操作,对于每一次染色操作是选择树上的一条路径,将路径上所有节点都染上第 z z z种颜色。
输出m次操作以后,每一个节点上染色次数最多的颜色。
1 < = n , m , z < = 1 e 5 1<=n,m,z <= 1e5 1<=n,m,z<=1e5


思路:

对于树上的路径操作,自然能够想到使用树链剖分,将树形结构转化为线形结构,并能够将树上的路径划分为几个连续的区间。

随后问题转化为:
在一个一维数轴上,进行m次染色,每次选择一个区间染成某一种颜色,问最后每一个点染色次数最多的颜色。

对于该问题,我们首先考虑一个简单的版本,即只存在一种颜色,我们如何求出该颜色在每一个点的染色次数呢?

考虑扫描线,即对于每一个区间 [ L , R ] [L,R] [L,R],我们在 L L L处加1, R + 1 R+1 R+1处减 1 1 1,最后扫一遍即可。

那对于多种颜色,虽然计算还是+1或者-1的计算,但为了区分不同的颜色,对于颜色 k k k,我们标记时使用 + k +k +k − k -k k

此时对于该题我们就有了清晰的思路,首先对于每一个点开一个vector,用于保存该点的标记。
随后对于每一次染色,使用树链剖分将路径转化为几个连续的区间,对于区间 [ L , R ] [L,R] [L,R]染第 k k k种颜色,我们就更新:

vector<int> vec[n+1];

for x in All_Segment:
	vec[x.L].push_back(x.k);
	vec[x.R].push_back(-x.k);

定义数组:Ans[i]:第i种节点当前的染色次数
按顺序遍历节点,随后遍历其对应的vector,更新Ans数组。

随后从Ans数组里面找出值最大且序号最小的下标即可。
这一步可以使用权值线段树进行优化。

此题得解。

代码:

#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define lson rt<<1
#define rson rt<<1|1

const int A = 1e5 + 10;
class Gra{
public:
    int v,next;
}G[A<<2];

class Seg_Tree{
public:
    int l,r,Mx;
}Tree[A<<2];

int n,m,head[A],tot,twt;
int fa[A],dep[A],siz[A],son[A],top[A],pos[A],ID[A],Ans[A];
vector<int> v[A];

void init(){
    for(int i=0 ;i<A ;i++){
        v[i].clear();
        head[i] = -1;
    }
    siz[0] = 0;
    tot = twt = 0;
}

void add(int u,int v){
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void dfs(int u,int pre,int d){
    fa[u] = pre;son[u] = -1;
    siz[u] = 1;dep[u] = d;

    for(int i=head[u] ; i!=-1 ;i=G[i].next){
        int v = G[i].v;
        if(v == pre) continue;
        dfs(v,u,d+1);
        siz[u] += siz[v];

        if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs(int u,int tp){
    pos[u] = ++twt;
    ID[twt] = u;
    top[u] = tp;

    if(son[u] == -1) return;
    dfs(son[u],tp);

    for(int i=head[u] ; i!=-1 ;i=G[i].next){
        int v = G[i].v;
        if(v == son[u] || v == fa[u]) continue;
        dfs(v,v);
    }
}

void Calc(int x,int y,int c){
    while(top[x] != top[y]){
        if(dep[top[x]] < dep[top[y]]) swap(x,y);

        v[pos[top[x]]].push_back(c);
        v[pos[x]+1].push_back(-c);

        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    v[pos[x]].push_back(c);
    v[pos[y]+1].push_back(-c);
}

void push_up(int rt){
    Tree[rt].Mx = max(Tree[lson].Mx,Tree[rson].Mx);
}

void build_Tree(int rt,int l,int r){
    Tree[rt].l = l;Tree[rt].r = r;
    Tree[rt].Mx = 0;

    if(l == r) return;
    int mid = (l+r)>>1;
    build_Tree(lson,l,mid);
    build_Tree(rson,mid+1,r);

    push_up(rt);
}

void update(int rt,int pos,int c){
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r){
        Tree[rt].Mx += c;
        return;
    }

    int mid = (l+r)>>1;
    if(pos <= mid) update(lson,pos,c);
    else           update(rson,pos,c);
    push_up(rt);
}

int query(int rt,int Mx){
    int l = Tree[rt].l,r = Tree[rt].r;
    if(l == r) return l;


    if(Tree[lson].Mx == Mx) return query(lson,Mx);
    else                    return query(rson,Mx);
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        if(n == 0 && m == 0) break;

        init();

        for(int i=1 ;i<n ;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            add(u,v);add(v,u);
        }

        dfs(1,1,1);
        dfs(1,1);

        int Mx = 0;
        for(int i=1 ;i<=m ;i++){
            int st,ed,col;
            scanf("%d%d%d",&st,&ed,&col);
            Calc(st,ed,col);
            Mx = max(Mx,col);
        }

        build_Tree(1,1,A);

        for(int i=1 ;i<=n ;i++){
            int Siz = v[i].size();
            for(int j=0 ;j<Siz ;j++){
                if(v[i][j] > 0) update(1,v[i][j],1);
                else            update(1,-v[i][j],-1);
            }
            if(Tree[1].Mx == 0) Ans[ID[i]] = 0;
            else                Ans[ID[i]] = query(1,Tree[1].Mx);
        }

        for(int i=1 ;i<=n ;i++){
            printf("%d\n",Ans[i]);
        }
    }
    return 0;
}

你可能感兴趣的:(ACM算法题目和总结)