洛谷 P3605 [USACO17JAN]Promotion Counting晋升者计数——树状数组,权值线段树

这个题目n^2的算法同样好想,usaco的测试数太给力啦,暴力竟然给70分。这个题目实际和上个题目类似,实际是求树上的逆序,所以将dfs序与树状数组结合起来。先将数据离散化。先做一次求值,搜索后再做一次求值,二者的差值就是这颗子树上的逆序。

#include <iostream>
#include <cstdio>
#include<algorithm>
using namespace  std;

const int maxn = 1e5+10;

int n,m,b[maxn],head[maxn],cur=1,ans[maxn],t[maxn];
struct EDGE {
	int to,next;
} edge[maxn];
void add(int u,int v) {
	edge[cur].to=v;
	edge[cur].next=head[u];
	head[u]=cur++;
}
struct COW {
	int v,id;
} a[maxn];
bool cmp(COW x,COW y) {
	return x.v>y.v;
};
int lowbit(int x) {
	return x&-x;
}
void update(int x,int v) {
	for(; x<n; x+=lowbit(x))t[x]+=v;
}
int query(int x) { //查询比他小的数的个数 ,1.。x的个数 ,实际是比他大的个数
	int ans=0;
	for(; x; x-=lowbit(x))ans+=t[x];
	return ans;
}


void dfs(int x) {
	update(b[x],1);
	ans[x]-=query(b[x]-1) ;
	for(int i=head[x]; i>0; i=edge[i].next)
		dfs(edge[i].to);
	ans[x]+=query(b[x]-1);

}

int main() {
	scanf("%d",&n);
	for(int i=1; i<=n; i++)scanf("%d",&a[i].v),a[i].id=i; //逆序排序后离散化
	sort(a+1,a+1+n,cmp);
	for(int i=1; i<=n; i++)b[a[i].id]=i;
	for(int i=2; i<=n; i++) {
		int f;
		scanf("%d",&f);
		add(f,i);
	}
	dfs(1);

	for(int i=1; i<=n; i++)printf("%d\n",ans[i]);
}

在网上看有你用线段树写的,实际是权值线段树。就又照着权值线段的模板写了一遍。还是能用树状数组就用它。

权值线段树的构建类似树状数组,将数据离散化后,标记数据i是否出现过,出现过,i的位置标志上1.

#include <iostream>
#include <cstdio>
#include<algorithm>
using namespace  std;

const int maxn = 1e5+10;

struct Node {
	int l,r,tot;
} tree[maxn*3];
int n,m,b[maxn],head[maxn],cur=1,ans[maxn];
struct EDGE {
	int to,next;
} edge[maxn];
void add(int u,int v) {
	edge[cur].to=v;
	edge[cur].next=head[u];
	head[u]=cur++;
}
struct COW {
	int v,id;
} a[maxn];
bool cmp(COW x,COW y) {
	return x.v<y.v;
};
void build(int l,int r,int o) {
	tree[o].l=l;
	tree[o].r=r;
	if(tree[o].l==tree[o].r) return ;
	int mid=(tree[o].l+tree[o].r)>>1;
	build(l,mid,o<<1);
	build(mid+1,r,o<<1|1);
}

void push_up(int o) {
	tree[o].tot=tree[o<<1].tot+tree[o<<1|1].tot;
}

void update(int o,int x) {
	if(tree[o].l==x && tree[o].l==tree[o].r) {
		tree[o].tot++;
		return ;
	}
	int mid=(tree[o].l+tree[o].r)>>1;
	if(x<=mid) update(o<<1,x);
	if(x>mid) update(o<<1|1,x);
	push_up(o);
}

int query(int o,int l,int r) { //查询>=l的数的个数, 也就是比当前数l-1大的数的个数 
	if(tree[o].l>r || tree[o].r<l) return 0;
	if(tree[o].l==l && tree[o].r==r) return tree[o].tot;
	int mid=(tree[o].l+tree[o].r)>>1;
	if(r<=mid) return query(o<<1,l,r);
	if(l>mid) return query(o<<1|1,l,r);
	return query(o<<1,l,mid)+query(o<<1|1,mid+1,r);
}

void dfs(int x) {
	update(1,b[x]);
	ans[x]-=query(1,b[x]+1,n) ;
	for(int i=head[x]; i>0; i=edge[i].next)
		dfs(edge[i].to);
	ans[x]+=query(1,b[x]+1,n);
}

int main() {
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	scanf("%d",&n);
	for(int i=1; i<=n; i++)scanf("%d",&a[i].v),a[i].id=i;
	sort(a+1,a+1+n,cmp);
	for(int i=1; i<=n; i++)b[a[i].id]=i;
	build(1,n,1);
	for(int i=2; i<=n; i++) {
		int f;
		scanf("%d",&f);
		add(f,i);
	}
	dfs(1);
	for(int i=1; i<=n; i++)printf("%d\n",ans[i]);
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值