黑暗爆炸 3307. 雨天的尾巴

链接

https://darkbzoj.tk/problem/3307

题意

树形图上每次分配给 a a a b b b 路径上的所有点一个 z z z 类物品,分配 m m m 次,求每个点拥有最多的物品的种类

思路

树上差分+权值线段树合并

c c c 数组为每个节点拥有每类物品的计数数组

b b b 数组是差分后的计数数组

对于每次分配,让 b [ x ] [ z ] b[x][z] b[x][z] 1 1 1 b [ y ] [ z ] b[y][z] b[y][z] 1 1 1 b [ l c a ( x , y ) ] [ z ] b[lca(x,y)][z] b[lca(x,y)][z] 1 1 1 b [ f a [ l c a ( x , y ) ] ] [ z ] b[fa[lca(x,y)]][z] b[fa[lca(x,y)]][z] 1 1 1

对于每个点都建立一颗权值线段树,维护 b b b 数组

而每个点的 c c c 数组:

c [ x ] = b [ x ] + ∑ i ∈ s o n ( x ) c [ i ] c[x]=b[x]+\sum \limits_{i \in son(x)} c[i] c[x]=b[x]+ison(x)c[i]

自上向下合并线段树即可算出 c c c 数组

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
int cnt,to[N*2],nxt[N*2],head[N];
int x[N],y[N],z[N];
int tot,mp[N],f[N][20],tt,depth[N],st[N],ans[N];
struct seg {
    int l=0,r=0;
    int mx=0;
    int id=0;
}tr[N*50];
int root[N],t;
void addedge(int a,int b) {
    cnt++;
    to[cnt]=b;
    nxt[cnt]=head[a];
    head[a]=cnt;
}
void pushup(int p) {
    int l=tr[p].l,r=tr[p].r;
    if(tr[l].mx>=tr[r].mx) tr[p].mx=tr[l].mx,tr[p].id=tr[l].id;
    else tr[p].mx=tr[r].mx,tr[p].id=tr[r].id;
}
void update(int &p,int l,int r,int x,int d) {
    if(!p) p=++t;
    if(l==r) {
        tr[p].mx+=d;
        tr[p].id=l;
        return;
    }
    int mid=l+r>>1;
    if(x<=mid) update(tr[p].l,l,mid,x,d);
    else update(tr[p].r,mid+1,r,x,d);
    pushup(p);
}
int merge(int p,int q,int l,int r) {
    if(!p) return q;
    if(!q) return p;
    if(l==r) {
        tr[p].mx+=tr[q].mx;
        tr[p].id=tr[p].mx?l:0;
        return p;
    }
    int mid=l+r>>1; 
    tr[p].l=merge(tr[p].l,tr[q].l,l,mid);
    tr[p].r=merge(tr[p].r,tr[q].r,mid+1,r);
    pushup(p);
    return p;
}
void bfs() {
    queue<int>q;
    q.push(1);
    depth[1]=1;
    while(q.size()) {
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=nxt[i]) {
            int v=to[i];
            if(depth[v]) continue;
            depth[v]=depth[u]+1;
            f[v][0]=u;
            for(int i=1;i<=tt;i++) f[v][i]=f[f[v][i-1]][i-1];
            q.push(v);
        }
    }
}
int lca(int a,int b) {
    if(depth[a]>depth[b]) swap(a,b);
    for(int i=tt;i>=0;i--) if(depth[f[b][i]]>=depth[a]) b=f[b][i];
    if(a==b) return a;
    for(int i=tt;i>=0;i--) if(f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
    return f[a][0];
}
void dfs(int u) {
    st[u]=-1;
    for(int i=head[u];i;i=nxt[i]) {
        int v=to[i];
        if(st[v]==-1) continue;
        dfs(v);
        root[u]=merge(root[u],root[v],1,tot);
    }
    ans[u]=mp[tr[root[u]].id];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    tt=(int)log(n)/log(2)+1;
    for(int i=1;i<=n;i++) root[i]=++t;
    for(int i=1;i<n;i++) {
        int a,b;
        cin>>a>>b;
        addedge(a,b);
        addedge(b,a);
    }
    bfs();
    for(int i=1;i<=m;i++) {
        cin>>x[i]>>y[i]>>z[i];
        mp[++tot]=z[i];
    }
    sort(mp+1,mp+1+tot);
    tot=unique(mp+1,mp+1+tot)-mp-1;
    for(int i=1;i<=m;i++) {
        int r=lca(x[i],y[i]);
        int w=lower_bound(mp+1,mp+1+tot,z[i])-mp;
        update(root[x[i]],1,tot,w,1);
        update(root[y[i]],1,tot,w,1);
        update(root[r],1,tot,w,-1);
        if(f[r][0]) update(root[f[r][0]],1,tot,w,-1);
    }
    dfs(1);
    for(int i=1;i<=n;i++) cout<<ans[i]<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值