jzoj5833 Endless Fantasy 树上启发式合并+线段树

Description


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

30%的数据,n<=1000;
60%的数据,n<=40000;
100%的数据,n<=400000,m<=n,1<=A_i<=m,0<=B_i<=1000。
输入文件较大请使用读入优化。

Solution


这是我比较顺手的一类题目

考虑离线询问。我只会两个做法,线段树合并和树上启发式合并
考虑到各种因素这里写了dsu on tree+线段树的没有梦想做法,实际可以用桶代替线段树来跑得更快
这种写法需要卡常

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#define rep(i,st,ed) for (register int i=st,_=ed;i<=_;++i)

const int N=400005;

struct edge {int y,next;} e[N*2+1];
struct data {int x,y;} ans[N];

std:: vector <data> rec[N];

int size[N],son[N],fa[N];
int max[N<<2],c[N],pos[N];
int ls[N],id[N],edCnt,n,m;

inline int read() {
    int x=0,v=1; char ch=getchar();
    for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
    for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
    return x*v;
}

inline void swap(int &x,int &y) {
    int z=x; x=y; y=z;
}

inline void add_edge(int x,int y) {
    e[++edCnt]=(edge) {y,ls[x]}; ls[x]=edCnt;
    e[++edCnt]=(edge) {x,ls[y]}; ls[y]=edCnt;
}

inline void dfs(int now) {
    size[now]=1;
    for (register int i=ls[now];i;i=e[i].next) {
        if (e[i].y==fa[now]) continue;
        fa[e[i].y]=now; dfs(e[i].y);
        size[now]+=size[e[i].y];
        if (size[e[i].y]>size[son[now]]) son[now]=e[i].y;
    }
}

inline void change(int x,int v) {
    for (register int i=0,_=rec[id[x]].size();i<_;++i) {
        data tmp=rec[id[x]][i];
        c[tmp.x]+=tmp.y*v;
        for (register int now=(pos[tmp.x]>>1);now;now>>=1) {
            int wjp=max[now];
            if (c[max[now<<1|1]]>c[max[now<<1]]) max[now]=max[now<<1|1];
            else max[now]=max[now<<1];
            if (wjp==max[now]&&wjp!=tmp.x) break;
        }
    }
}

inline void solve(int now) {
    for (register int i=ls[now];i;i=e[i].next) {
        if (e[i].y!=fa[now]&&e[i].y!=son[now]) {
            solve(e[i].y);
            change(e[i].y,-1);
        }
    }
    if (son[now]) solve(son[now]);
    for (register int i=ls[now];i;i=e[i].next) {
        if (e[i].y!=fa[now]&&e[i].y!=son[now]) {
            change(e[i].y,1);
        }
    }
    change(now,1);
    ans[now]=(data){max[1],c[max[1]]};
    for (register int i=ls[now];i;i=e[i].next) {
        if (e[i].y!=fa[now]) {
            if (rec[id[now]].size()<rec[id[e[i].y]].size()) swap(id[now],id[e[i].y]);
            int a=id[now],b=id[e[i].y];
            for (register int j=0,_=rec[b].size();j<_;++j) {
                rec[a].push_back(rec[b][j]);
            }
            id[e[i].y]=0;
        }
    }
}

inline void build(int now,int tl,int tr) {
    if (tl==tr) {
        pos[tl]=now;
        return (void) (max[now]=tl);
    }
    int mid=(tl+tr)>>1;
    build(now<<1,tl,mid);
    build(now<<1|1,mid+1,tr);
}

int main(void) {
    freopen("endless.in","r",stdin);
    freopen("endless.out","w",stdout);
    n=read(),m=read();
    build(1,1,m);
    rep(i,2,n) add_edge(read(),read());
    dfs(1);
    rep(i,1,n) {
        rec[i].push_back((data){read(),read()});
        id[i]=i;
    }
    solve(1);
    rep(i,1,n) printf("%d %d\n", ans[i].x,ans[i].y);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值