AcWing353 经典的树上差分+动态开点+权值线段树合并

#include <bits/stdc++.h>

using namespace std;
#define ll long long
#define mem(a, b) memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
#define DBG printf("this is a input\n")
#define fi first
#define se second
#define mk(a, b) make_pair(a,b)
#define p_queue priority_queue

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}
int n , m, vis[200005];
int head[200005], cnt;
struct e{
    int t, next;
}edge[200005];
struct question
{
    int x, y ,z;
}qes[200005];
void add(int f, int t)
{
    edge[cnt].t =t ;
    edge[cnt].next = head[f];
    head[f] = cnt ++;
}
int bz[100005][30] , dep[100005];
int idx[100005];
void dfs(int u, int deep)
{
    dep[u] = deep;
    for(int i = head[u] ; i != -1 ; i = edge[i].next) {
        int v = edge[i].t;
        if (!dep[v]){
            bz[v][0] = u;
            dfs(v, deep+1);
        }
    }
}
void deal()
{
    for(int i = 1 ; i <= 25 ; i ++)
        for(int j = 1 ; j <= n ; j ++)
            bz[j][i] = bz[bz[j][i-1]][i-1];
}
int LCA(int x, int y)
{
    if(dep[x] < dep[y])
        swap(x,y);
    for(int i = 25 ; i >= 0 ; i --)
        if(dep[bz[x][i]] >= dep[y])
            x = bz[x][i];
    if (x == y)
        return x;
    for(int i = 25 ; i >= 0 ; i --)
    {
        if(bz[x][i] ^ bz[y][i])
            x = bz[x][i] , y = bz[y][i];
    }
    return bz[x][0];
}
int rt[200005], ans[200005];
int tot, len;
struct sg{
    int lc, rc, mx ,id;
}tree[200005*80];
void pushup(int id)
{
    int lc = tree[id].lc, rc = tree[id].rc;
    if(tree[lc].mx >= tree[rc].mx) tree[id].mx = tree[lc].mx, tree[id].id = tree[lc].id;
    else tree[id].mx = tree[rc].mx , tree[id].id = tree[rc].id;
}
void update(int &id, int l ,int r, int val, int c)
{
    if (!id) id = ++ tot;//动态编号,类似主席树
    if(l == r) {
        tree[id].mx += c, tree[id].id = l;
        return ;
    }
    int mid = (l + r) >> 1;
    //这里是动态开点,每次只会执行一半
    if(val <= mid) update(tree[id].lc,l,mid,val,c);
    else update(tree[id].rc,mid+1,r,val,c);
    pushup(id);
}
int merge(int x , int y, int l , int r)
{
    //这里是区间合并,把深度大的朝小的合并,前缀和思想
    if(!x) return y;
    if(!y) return x;
    if(l == r){
        tree[x].mx += tree[y].mx;
        return x;
    }
    int mid = (l + r) >> 1;
    tree[x].lc = merge(tree[x].lc , tree[y].lc , l ,mid);
    tree[x].rc = merge(tree[x].rc , tree[y].rc , mid+1 , r);
    pushup(x);
    return x;
}
void getans(int u)
{
    vis[u] = 1;
    for(int i = head[u] ; i != -1; i = edge[i].next)
    {
        int v = edge[i].t;
        if(!vis[v])
        {
            getans(v);
            rt[u] = merge(rt[u], rt[v], 1, len);
        }
    }
    ans[u] = tree[rt[u]].mx == 0 ? 0 : tree[rt[u]].id;
}
int main(void)
{
    mem(head,-1);
    scanf("%d %d",&n,&m);
    int u , v , z;
    for(int i = 1 ; i < n ; i ++)
    {
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs(1,1);
    deal();
    for(int i = 1 ; i <= m ; i ++)
        scanf("%d %d %d",&qes[i].x,&qes[i].y,&qes[i].z), idx[i] = qes[i].z;
    //对救济种类离散化
    sort(idx+1,idx+1+m);
    len = unique(idx+1,idx+1+m)-idx-1;
    //树上差分经典操作
    //每一个树上节点都开一颗权值线段树,权值线段树用于维护差分数组
    //由于采用动态开点,所以每一颗线段树的空间为LogN,遍历复杂度也为LogN
    for(int i = 1 ; i <= m ; i ++)
    {
        u = qes[i].x , v = qes[i].y, z = qes[i].z;
        int lca = LCA(u,v);
        int zt = lower_bound(idx+1,idx+1+len,z)-idx;
        update(rt[u],1,len,zt,1);
        update(rt[v],1,len,zt,1);
        update(rt[lca],1,len,zt,-1);
        update(rt[bz[lca][0]],1,len,zt,-1);
    }
    //树上差分获得序列的基本操作,这里是权值线段树的合并
    getans(1);
    for(int i = 1 ; i <= n ; i ++)
        printf("%d\n",idx[ans[i]]);
}

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值