[线段树合并]luogu P1552 [APIO2012]派遣

题面

https://www.luogu.com.cn/problem/P1552

分析

容易想到用可维护单调性的数据结构维护一个子树内的可雇佣的最大忍者数量

自下而上对每个节点计算 $l_i /times num$ ,合并子树的线段树

可以用可并堆,但是不想写左偏树,写了比较简单的线段树合并

为了卡过时间限制,需要对每个忍者的薪水做离散化,保证线段树中每个下标只出现一次,合并总复杂度为 $O(nlogn)$

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct Graph {
    int v,nx;
}g[N];
int cnt,list[N],w[N],a[N],l[N],rev[N],root[N],sz[20*N],c[20*N][2],tcnt;
int n,m;
ll ans,t[20*N];

void Add(int u,int v) {g[++cnt]=(Graph){v,list[u]};list[u]=cnt;}

void Insert(int &x,int l,int r,int k,int v) {
    if (!x) x=++tcnt;
    t[x]=v;sz[x]++;
    if (l==r) return;
    int mid=l+r>>1;
    if (k<=mid) Insert(c[x][0],l,mid,k,v); else Insert(c[x][1],mid+1,r,k,v);
}

void Merge(int &x,int y,int l,int r) {
    if (!x) {x=y;return;}
    if (!y) return;
    t[x]+=t[y];sz[x]+=sz[y];
    if (l==r) return;
    int mid=l+r>>1;
    Merge(c[x][0],c[y][0],l,mid);Merge(c[x][1],c[y][1],mid+1,r);
}

int Get_Size(int x,int l,int r,int lim) {
    if (t[x]<=lim) return sz[x];
    if (l==r) return 0;
    int mid=l+r>>1;
    if (lim<t[c[x][0]]) return Get_Size(c[x][0],l,mid,lim);
    return sz[c[x][0]]+Get_Size(c[x][1],mid+1,r,lim-t[c[x][0]]);
}

void DFS(int u) {
    Insert(root[u],1,n,rev[u],w[u]);
    for (int i=list[u];i;i=g[i].nx) DFS(g[i].v),Merge(root[u],root[g[i].v],1,n);
    ans=max(ans,1ll*l[u]*Get_Size(root[u],1,n,m));
}

int main() {
    scanf("%d%d",&n,&m);
    for (int i=1,b;i<=n;i++) scanf("%d%d%d",&b,&w[i],&l[i]),Add(b,i),a[i]=w[i];
    sort(a+1,a+n+1);for (int i=1;i<=n;i++) rev[i]=lower_bound(a+1,a+n+1,w[i])-a,a[rev[i]]--;
    DFS(1);
    printf("%lld",ans);
}
View Code

 

posted @ 2021-03-15 21:00  Vagari  阅读(83)  评论(0编辑  收藏  举报