线段树合并 大根堆
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; }