jzoj 5833.【省选模拟8.20】Endless Fantasy 树上启发式合并

Description

中二少年cenbo幻想自己统治着Euphoric Field。由此他开始了Endless Fantasy。
Euphoric Field有 n n 座城市,m个民族。这些城市之间由 n1 n − 1 条道路连接形成了以城市 1 1 为根的有根树。每个城市都是某一民族的聚居地,cenbo知道第i个城市的民族是Ai,人数是 Bi B i 。为了维护稳定,cenbo需要知道某个区域内人数最多的民族。他向你提出 n n 个询问,其中第i个询问是:求以 i i 为根的子树内,人数最多的民族有是哪个,这个民族有多少人。如果子树内人数最多的民族有多个,输出其中编号最小的民族。

Input

输入文件endless.in共有2n行。
第一行有两个整数 n,m n , m
接下来 n1 n − 1 行,每行有两个整数 u,v u , v ,表示一条连接 u u v的道路。
接下来 n n 行,第i行有两个整数Ai,Bi

Output

输出文件endless.out共有 n n 行。
第i行两个整数x,y,分别表示以 i i 为根的子树中人数最多的民族和它的人数。

Sample Input

8 6
1 2
1 3
2 4
4 5
3 6
5 7
1 8
2 8
2 5
1 1
3 1
6 7
5 6
1 10
4 6

Sample Output

2 13
1 10
5 6
1 10
1 10
5 6
1 10
4 6

Data Constraint

30%的数据,n<=1000
60%的数据, n<=40000 n <= 40000
100%的数据, n<=400000m<=n1<=Ai<=m0<=Bi<=1000 n <= 400000 , m <= n , 1 <= A i <= m , 0 <= B i <= 1000
输入文件较大请使用读入优化。

分析:
我们给每个棵子树维护一个桶。
因为没有修改,且是给所有点询问。考虑启发式合并这个桶即可。父亲的桶从重儿子获得,其他子树暴力加上。先跑轻儿子,然后把桶清空(再跑一遍子树)即可。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>

const int maxn=4e5+7;

using namespace std;

int n,m,cnt,x,y;
int a[maxn],b[maxn],size[maxn],ans[maxn][2],ls[maxn];
int h[maxn];

struct edge{
    int y,next;
}g[maxn*2];

void add(int x,int y)
{
    g[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}

void dfs(int x,int fa)
{
    size[x]=1;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (g[i].y==fa) continue;
        dfs(y,x);
        size[x]+=size[y];
    }
}

void ins(int x,int y,int d,int op)
{
    if (op==1)
    {
        h[x]+=y;
        if ((h[x]>ans[d][1]) || ((h[x]==ans[d][1]) && (x<ans[d][0])))
        {
            ans[d][1]=h[x];
            ans[d][0]=x;
        }
    }
    else h[x]-=y;
}

void count(int x,int fa,int d,int op)
{
    ins(a[x],b[x],d,op);
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        count(y,x,d,op);
    }
}

void solve(int x,int fa)
{
    int c=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (y==fa) continue;
        if (size[y]>size[c]) c=y;
    }
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((y==fa) || (y==c)) continue;
        solve(y,x);
        count(y,x,x,0);
    }
    if (c) solve(c,x),ans[x][1]=ans[c][1],ans[x][0]=ans[c][0];
    ins(a[x],b[x],x,1);
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if ((y==fa) || (y==c)) continue;
        count(y,x,x,1);
    }
}

int main()
{
    freopen("endless.in","r",stdin);
    freopen("endless.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    for (int i=1;i<=n;i++) scanf("%d %d",&a[i],&b[i]);
    dfs(1,0);   
    solve(1,0);
    for (int i=1;i<=n;i++) printf("%d %d\n",ans[i][0],ans[i][1]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值