zoukankan      html  css  js  c++  java
  • P1552 [APIO2012]派遣

    https://www.luogu.com.cn/problem/P1552
    线段树合并

    题意就是,在一棵树中,每个点有一个花费,还有一个权值,需要找出一堆点,使得它们的花费总和小于等于 (m),且全都在同一子树中,最大化点的个数乘以这个子树的根的权值
    子树的根可以不选

    枚举子树的根,肯定是选子树中前 (k) 小花费的点,可以用类似与前缀和的二分来做
    但是如果只是前缀和,那么没做一次就都得 (O(n)),因为无法通过儿子的前缀和加上其他某些信息来推知父亲的前缀和
    所以要用到线段树,以花费为下标键动态开点线段树,从线段树上二分,并从儿子往父亲合并

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN puts("")
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 100006
    int n,m;
    struct Node{
    	Node *ls,*rs;
    	long long sum;
    	int cnt;
    }dizhi[N*80],*root[N],*null=&dizhi[0];
    int tot;
    int fa[N],L[N],C[N];
    inline void New(Node *&a){
    	a=&dizhi[++tot];a->ls=a->rs=null;
    }
    void change(Node *tree,int l,int r,int pos){
    	tree->sum+=pos;tree->cnt++;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(pos<=mid){
    		if(tree->ls==null) New(tree->ls);
    		change(tree->ls,l,mid,pos);
    	}
    	else{
    		if(tree->rs==null) New(tree->rs);
    		change(tree->rs,mid+1,r,pos);
    	}
    }
    Node *merge(Node *a,Node *b,int l,int r){
    	if(a==null) return b;
    	if(b==null) return a;
    	if(l==r){
    		a->sum+=b->sum;a->cnt+=b->cnt;
    		return a;
    	}
    	int mid=(l+r)>>1;
    	a->ls=merge(a->ls,b->ls,l,mid);
    	a->rs=merge(a->rs,b->rs,mid+1,r);
    	a->sum=a->ls->sum+a->rs->sum;
    	a->cnt=a->ls->cnt+a->rs->cnt;
    	return a;
    }
    int ask(Node *tree,int l,int r,int lim){
    	if(tree==null) return 0;
    	if(l==r) return lim>=tree->sum;
    	int mid=(l+r)>>1;
    	if(lim>=tree->ls->sum) return tree->ls->cnt+ask(tree->rs,mid+1,r,lim-tree->ls->sum);
    	return ask(tree->ls,l,mid,lim);
    }
    int main(){
    	n=read();m=read();
    	for(reg int i=1;i<=n;i++){
    		fa[i]=read();C[i]=read();L[i]=read();
    		New(root[i]);
    		change(root[i],1,1e9,C[i]);
    	}
    	long long ans=0;
    	for(reg int i=n;i;i--){//题目中明确了一个点的父亲编号比他小
    		ans=std::max(ans,(long long)ask(root[i],1,1e9,m)*L[i]);
    		if(fa[i]) merge(root[fa[i]],root[i],1,1e9);
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    Handsontable添加超链接
    Handsontable 筛选事件
    handsontable自定义渲染
    M1 Mac安装 Homebrew
    Pypi官网怎么找历史依赖包
    在 CentOS7 中我们在安装 MySQL
    Ansible使用yum安装
    Ansible集群自动化运维操作
    java对list中map集合中某个字段排序
    使用hive的orcfiledump命令查看orc文件
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13841969.html
Copyright © 2011-2022 走看看