zoukankan      html  css  js  c++  java
  • 线段树合并 大根堆

    C. 大根堆

    内存限制:256 MiB 时间限制:1000 ms 标准输入输出
    题目类型:传统 评测方式:文本比较
     

    题目描述

    给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。 你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

    输入格式

    第一行包含一个正整数n(1<=n<=200000),表示节点的个数。 接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9,1<=p_i<i,p_1=0),表示每个节点的权值与父亲。

    输出格式

    输出一行一个正整数,即最多的点数。

    样例

    样例输入

    6
    3 0
    1 1
    2 1
    3 1
    4 1
    5 1
    

    样例输出

    5
    

    析:线段树合并题,但是此题的权值线段树存储的是合法的方案数,并不是值域。
    显然,应该从叶子节点开始(也就是递归)向上更新值,每个点有选或不选两种可能;
    说一下卡住我的几个地方:首先,记录每个点选或不选的方案数,刚开始,我想建两颗树来存,但是打起来比较麻烦,所以,我先将线段树合并,然后再考虑选或不选的问题
    若选,最大值为:ans1=query(rt[x],1,cnt,v[x]-1)+1,若不选,最小值为ans2=query(rt[x],1,cnt,v[x]);
    所以,若ans1<=ans2,显然可以不选,直接return;
    第二个地方就是选择后的更新,通过二分找到一个最大的区间,满足 query(rt[x],1,cnt,mid)<ans1 ,所以,在[v[x],mid]区间的合法方案数+1
    注意,因为线段树存储的是方案数,所以不能 pushup,各节点之间独立。
    在查询过程中,由于之前的updata, sum的值发生了变化,所以要加上,并且计算左右儿子
    最后,注意一个卡了我好久的细节,注意线段树开点的时候的取地址符!!

    代码:
    #include<bits/stdc++.h>
    #define re register int
    using namespace std;
    const int N=2e5+10;
    const int INF=1e8;
    int n,tot,cnt,num;
    int maxx=-INF;
    int v[N],v2[N];
    int rt[N];
    int to[N<<1],next[N<<1],head[N<<1];
    struct CUN
    {
        int lc,rc,sum;
    }use[N*40];
    int merge(int p,int q,int l,int r)
    {
        if(!p)
            return q;
        if(!q)
            return p;
        use[p].sum+=use[q].sum;
        int mid=(l+r)>>1;
        use[p].lc=merge(use[p].lc,use[q].lc,l,mid);
        use[p].rc=merge(use[p].rc,use[q].rc,mid+1,r);
        return p;
    }
    int query(int p,int l,int r,int z)
    {
        if(!p)
            return 0;
        int mid=(l+r)>>1;
        int o=use[p].sum;
        if(z<=mid)
            o+=query(use[p].lc,l,mid,z);
        else
            o+=query(use[p].rc,mid+1,r,z);
        return o;
    }
    void updata(int &p,int L,int R,int l,int r,int z)
    {
        if(!p)
            p=++num;
        if(l<=L&&R<=r)
        {
            use[p].sum+=z;
            return;
        }
        int mid=(L+R)>>1;
        if(l<=mid)
            updata(use[p].lc,L,mid,l,r,z);
        if(r>mid)
            updata(use[p].rc,mid+1,R,l,r,z);
    }
    void dfs(int x)
    {
        int ans1=0,ans2=0;
        for(re i=head[x];i;i=next[i])
        {
            int p=to[i];
            dfs(p);
            rt[x]=merge(rt[x],rt[p],1,cnt);
        }
        ans1=query(rt[x],1,cnt,v[x]-1)+1;
        ans2=query(rt[x],1,cnt,v[x]);
        if(ans1<=ans2)
            return;
        int l=v[x],r=cnt,pos=v[x];
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(query(rt[x],1,cnt,mid)<ans1)
            {
                l=mid+1;
                pos=mid;
            }
            else
                r=mid-1;
        }
        updata(rt[x],1,cnt,v[x],pos,1);
    }
    void add(int x,int y)
    {
        to[++tot]=y;
        next[tot]=head[x];
        head[x]=tot;
    }
    int main()
    {
        scanf("%d",&n);
        int a,b;
        for(re i=1;i<=n;i++)
        {
            scanf("%d%d",&a,&b);
            v[i]=a;
            v2[i]=v[i];
            add(b,i);    
        }
        sort(v2+1,v2+n+1);
        cnt=unique(v2+1,v2+n+1)-v2-1;
        for(re i=1;i<=n;i++)
            v[i]=lower_bound(v2+1,v2+cnt+1,v[i])-v2;
        dfs(1);
        printf("%d\n",query(rt[1],1,cnt,cnt));
        return 0;
    }

    
    

    E. 领导集团问题

    内存限制:256 MiB 时间限制:1000 ms 标准输入输出
    题目类型:传统 评测方式:文本比较
    
    
     
    
    

    题目描述

    一个公司的组织领导架构可以用一棵领导树来表示。公司的每个成员对应于树中一个结点

      ,且每个成员都有响应的级别    。越高层的领导,其级别值    越小。树中任何两个结点之间有边相连,则表示与结点相应的两个成员属于同一部门。领导集团问题就是根据公司的领导树确定公司的最大部门。换句话说,也就是在领导树中寻找最大的部门结点子集,使得的结点      ,如果      的子孙结点,则 

    编程任务:对于任意对于给定的领导树,计算出领导树中最大的部门结点子集。

    
    

    输入格式

    第一行有一个正整数

     ,表示领导树的结点数。接下来的一行中有   个整数。第   个数表示    。再接下来的     行中,第   行有一个整数    表示   

    的双亲结点。

    
    
    
     为正整数,    

    
    

    输出格式

    输出找到的最大的部门的成员数。

    
    

    样例

    样例输入

    6
    2 5 1 3 5 4
    1
    1
    2
    2
    4
    

    样例输出

    4
    
    同上,这道题就是求一个小根堆,我们只需要将上面的操作该一改就好了
    #include<bits/stdc++.h>
    #define re register int
    #define net next
    using namespace std;
    const int N=2e5+10;
    const int INF=1e8;
    int n,tot,cnt,num;
    int maxx=-INF;
    int v[N],v2[N];
    int rt[N];
    int to[N<<1],next[N<<1],head[N<<1];
    struct CUN
    {
    	int lc,rc,sum; 
    }use[N*40];
    void add(int x,int y)
    {
    	to[++tot]=y;
    	net[tot]=head[x];
    	head[x]=tot;
    }
    int merge(int p,int q,int l,int r)
    {
    	if(!p)
    		return q;
    	if(!q)
    		return p;
    	use[p].sum+=use[q].sum;
    	int mid=(l+r)>>1;
    	use[p].lc=merge(use[p].lc,use[q].lc,l,mid);
    	use[p].rc=merge(use[p].rc,use[q].rc,mid+1,r);
    	return p;
    }
    int query(int p,int l,int r,int z)
    {
    	if(!p)
    		return 0;
    	int mid=(l+r)>>1;
    	int o=use[p].sum;
    	if(z>mid)
    		o+=query(use[p].rc,mid+1,r,z);
    	else
    		o+=query(use[p].lc,l,mid,z);
    	return o;
    }
    void updata(int &p,int L,int R,int l,int r,int z)
    {
    	if(!p)
    		p=++num;
    	if(l<=L&&R<=r)
    	{
    		use[p].sum+=z;
    		return;
    	}
    	int mid=(L+R)>>1;
    	if(l<=mid)
    		updata(use[p].lc,L,mid,l,r,z);
    	if(r>mid)
    		updata(use[p].rc,mid+1,R,l,r,z);
    }
    void dfs(int x,int fa)
    {
    	for(re i=head[x];i;i=net[i])
    	{
    		int p=to[i];
    		if(p==fa)
    			continue;
    		dfs(p,x);
    		rt[x]=merge(rt[x],rt[p],1,cnt);
    	}
    	int ans1=0,ans2=0;
    	ans1=query(rt[x],1,cnt,v[x])+1;
    	ans2=query(rt[x],1,cnt,v[x]-1);
    	if(ans1<=ans2)
    		return;
    	int l=0,r=v[x],pos=v[x];
    	while(l<=r)
    	{
    		int mid=(l+r)>>1;
    		if(query(rt[x],1,cnt,mid)<ans1)
    		{
    			pos=mid;
    			r=mid-1;
    		}
    		else
    			l=mid+1;
    	}
    	updata(rt[x],1,cnt,pos,v[x],1);
    }
    int main()
    {
    	scanf("%d",&n);
    	for(re i=1;i<=n;i++)
    	{
    		scanf("%d",&v[i]);
    		v2[i]=v[i];
    	}
    	int a;
    	for(re i=1;i<n;i++)
    	{
    		scanf("%d",&a);
    		add(a,i+1);
    		add(i+1,a);
    	}
    	sort(v2+1,v2+n+1);
    	cnt=unique(v2+1,v2+n+1)-v2-1;
    	for(re i=1;i<=n;i++)
    		v[i]=lower_bound(v2+1,v2+cnt+1,v[i])-v2;
    	dfs(1,0);
    	printf("%d\n",query(rt[1],1,cnt,1));
    	return 0;
    }
    
    
    
    





  • 相关阅读:
    caffe for python (官方翻译)
    实验三、页式地址重定位模拟
    实验二、银行家算法
    实验一:进程调度实验
    植物大战僵尸作弊器源代码(MFC版)
    植物大战僵尸作弊器源代码(控制台版)
    CE寻找游戏基址
    植物大战僵尸内存地址(转)
    Detour的简单使用
    C/S模型之命名管道
  • 原文地址:https://www.cnblogs.com/WindZR/p/14849696.html
Copyright © 2011-2022 走看看