• 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;
    }
    
  • 相关阅读:
    Centos7.0 安装Oralce 11g数据库
    python学习:基础数据类型
    Centos7.0 安装MySQL数据库
    Centos7.0 安装MariaDB数据库
    微服务的详情
    Class -- 10 -- Method类常用方法解析
    遍历list的三种方式
    使用@Autowired注解警告Field injection is not recommended
    java中的两种排序工具Arrays和Collections的使用
    java的reflection和introspector
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/13841969.html
Copyright © 2020-2023  润新知