zoukankan      html  css  js  c++  java
  • CodeForces 600E Lomsat gelral(线段树合并)

    题目链接:http://codeforces.com/problemset/problem/600/E

    You are given a rooted tree with root in vertex 1. Each vertex is coloured in some colour.

    Let's call colour c dominating in the subtree of vertex v if there are no other colours that appear in the subtree of vertex v more times than colour c. So it's possible that two or more colours will be dominating in the subtree of some vertex.

    The subtree of vertex v is the vertex v and all other vertices that contains vertex v in each path to the root.

    For each vertex v find the sum of all dominating colours in the subtree of vertex v.

    Input

    The first line contains integer n (1 ≤ n ≤ 105) — the number of vertices in the tree.

    The second line contains n integers ci (1 ≤ ci ≤ n), ci — the colour of the i-th vertex.

    Each of the next n - 1 lines contains two integers xj, yj (1 ≤ xj, yj ≤ n) — the edge of the tree. The first vertex is the root of the tree.

    Output

    Print n integers — the sums of dominating colours for each vertex.

    Examples

    Input
    4
    1 2 3 4
    1 2
    2 3
    2 4
    Output
    10 9 3 4
    Input
    15
    1 2 3 1 2 3 3 1 1 3 2 2 1 2 3
    1 2
    1 3
    1 4
    1 14
    1 15
    2 5
    2 6
    2 7
    3 8
    3 9
    3 10
    4 11
    4 12
    4 13
    Output
    6 5 4 3 2 3 3 1 1 3 2 2 1 2 3
    题目大意:输入N,有N个结点,接下来N个数代表每个结点的颜色,接下来N-1行,每行两个数u v 代表他们之间有边,保证是一颗树,规定根为1
    问你以每个结点为根,出现颜色最多的数的值的和为多少,(可能有多个数出现一样多)
    思路:完全是一道线段树合并的板子题,打比赛的时候没时间看,补题的时候想过多颗树合并,但是觉得会超时,就没有写,看题解,发现就是线段树合并
    这里给出复杂度,假设有n个结点,值域是m 则复杂度为:O(nlogm+N),证明我也不清楚 ,在这里是以权值建树
    看代码:
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cstdio>
    using namespace std;
    typedef long long LL;
    #define lson tr[now].l
    #define rson tr[now].r
    const int maxn=1e5+5;
    const int maxm=50;
    LL N;
    LL cnt=0,num=0;
    LL a[maxn];
    LL head[maxn];
    LL root[maxn];
    LL anss[maxn];
    struct Edge
    {
        LL to,nxt;
    }e[maxn<<1];
    struct Tree
    {
        LL l,r,sum,val,ans;//左右儿子,该种颜色的个数 ,对应的值 此时的答案
    }tr[maxn<<5];
    void add_edge(LL u,LL v)
    {
        e[++cnt].to=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    void Push_up(LL now)
    {
        if(tr[lson].sum>tr[rson].sum)//个数最多的在左子树
        {
            tr[now].ans=tr[lson].ans;
            tr[now].sum=tr[lson].sum;
            tr[now].val=tr[lson].val;
        }
        if(tr[lson].sum<tr[rson].sum)//个数最多的在右左子树
        {
            tr[now].ans=tr[rson].ans;
            tr[now].sum=tr[rson].sum;
            tr[now].val=tr[rson].val;
        }
        if(tr[lson].sum==tr[rson].sum)//个数最多的在左右子树
        {
            tr[now].ans=tr[lson].ans+tr[rson].ans;//答案是总和
            tr[now].sum=tr[lson].sum;//个数不变
            tr[now].val=tr[lson].val;//值无所谓
        }
    }
    LL Merge(LL x,LL y,LL l,LL r)
    {
        if(!x) return y;//另一颗数为空 合并之后肯定还是本身
        if(!y) return x;
        if(l==r)//到了叶子节点
        {
            tr[x].sum+=tr[y].sum;//更新和
            tr[x].ans=l;
            tr[x].val=l;
            return x;
        }
        LL mid=(l+r)>>1;
        tr[x].l=Merge(tr[x].l,tr[y].l,l,mid);
        tr[x].r=Merge(tr[x].r,tr[y].r,mid+1,r);
        Push_up(x);
        return x;
    }
    void Update(LL &now,LL l,LL r,LL pos,LL v)
    {
        if(!now) now=++num;//注意num的初值是N
        if(l==r)
        {
            tr[now].sum+=v;
            tr[now].ans=l;
            tr[now].val=l;
            return ;
        }
        LL mid=(l+r)>>1;
        if(pos<=mid) Update(lson,l,mid,pos,v);
        else Update(rson,mid+1,r,pos,v);
        Push_up(now);
    }
    void dfs(LL x,LL pre)
    {
        for(LL i=head[x];i!=-1;i=e[i].nxt)
        {
            LL v=e[i].to;
            if(v==pre) continue ;
            dfs(v,x);
            Merge(root[x],root[v],1,N);//把它的子树和本身进行合并
        }
        Update(root[x],1,N,a[x],1);//该节点本身也有值,所以得更新
        anss[x]=tr[root[x]].ans;
    }
    int main()
    {
        scanf("%lld",&N);
        num=N;
        for(int i=1;i<=N;i++)
        {
            scanf("%lld",&a[i]);
            root[i]=i;//每个结点对应一棵树
            head[i]=-1;
        }
        for(int i=1;i<N;i++)
        {
            LL u,v;scanf("%lld%lld",&u,&v);
            add_edge(u,v);add_edge(v,u);
        }
        dfs(1,0);
        for(int i=1;i<=N;i++) printf("%lld ",anss[i]);cout<<endl;
        return 0;
    }
    当初的梦想实现了吗,事到如今只好放弃吗~
  • 相关阅读:
    异步、作用域、闭包--setTimeout在for循环中的思考
    C++中*和&的定义和使用
    利用border设置transparent绘制图形
    暑假周记四
    暑假周记三
    win10系统下安装Linux虚拟机以及在虚拟机上安装Ubuntu
    暑假周记二
    暑假周记一
    《浪潮之巅》上——阅读笔记06
    《浪潮之巅》上——阅读笔记05
  • 原文地址:https://www.cnblogs.com/caijiaming/p/12000925.html
Copyright © 2011-2022 走看看