西南科技大学第九届程序设计竞赛B题解题报告(线段树版)

题目链接http://acm.swust.edu.cn/oj/contest/58/908/

题目大意:给你N个物品,每个物品有两个属性V1,V2,把它插入到k+1的位置(类似数组的插入),求它前一个物品的V1,不存在输出-1,和后一个物品的V2,不存在输出-1。

解题思路,可以维护一颗线段树,求得最后物品的序列,题目中样例最终的序列就是4,1,5,2,3,。表示第1个物品最终在4这个位置,第2个物品最终在1这个位置……

怎么使用线段树求得这个序列呢?

这个题有一个重要的性质,就是你把这个序列倒着看,最后一个插入的物品的位置一定是最终位置,不会改变!

因此我们维护一颗线段树,线段树节点记录区间还有多少的空位,我们倒着插入这个序列,最终得到的序列一定是正确的!

拿样例来举例,第5个物品先插入第3个位置(我定义的位置是1到n,转化一下即可),如图

                                                    1 - 5

                                                ↙        ↘

                                            1-3             4-5

                                         ↙     ↘     ↙      ↘

                                     1-2         3(5)  4           5

                                   ↙  ↘

                                 1        2

然后是4插入第2个位置

                                                 

                                                    1 - 5

                                                ↙        ↘

                                            1-3             4-5

                                         ↙     ↘     ↙      ↘

                                     1-2         3(5)  4           5

                                   ↙  ↘

                                 1        2(4)

然后是3插入第2位置

                                                     1 - 5

                                                ↙        ↘

                                            1-3             4-5

                                         ↙     ↘     ↙      ↘

                                     1-2         3(5)  4(3)       5

                                   ↙  ↘

                                 1        2(4)

后面的也一样了

得到这个序列以后,就可以顺着看这个序列了,每次查询它前面和后面有没有物品,然后插入这个物品。

这个时候又维护一颗线段树,节点记录插入的物品数,由于相对位置已经给出,查询的时候二分即可。

具体实现如下

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
#define lc l,m,index<<1
#define rc m+1,r,index<<1|1
#define N 20005
int cnt[N<<2];//节点信息
int ord[N];//相对序列
int aim[N];//绝对序列
int ex[N];//绝对位置是哪个物品
int v1[N],v2[N];//物品属性
int n;
void build(int l,int r,int index)//建第一颗树
{
	int m=(l+r)>>1;
	cnt[index]=r-l+1;
	if(l==r)return;
	build(lc);
	build(rc);
}
int query(int id,int l,int r,int index)//第一颗树的查询和维护
{
	int m=(l+r)>>1;
	cnt[index]--;
	if(l==r)return l;
	if(id>=cnt[index<<1])return query(id-cnt[index<<1],rc);  
    else return query(id,lc);  
}
int query2(int id,int l,int r,int index)//第二棵树的查询
{
	if(id==-1||!cnt[index])return -1;
	if(l==r)return l;
	int m=(l+r)>>1;
	if(id>=cnt[index<<1])return query2(id-cnt[index<<1],rc);
	else return query2(id,lc);
}
void updata(int id,int l,int r,int index)//第二棵树的维护
{
	int m=(l+r)>>1;
	cnt[index]++;
	if(l==r)return;
	if(id<=m)updata(id,lc);
	else updata(id,rc);
}
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		int i,pos;
		build(1,n,1);//第一颗树
		for(i=1;i<=n;i++)
		{
			scanf("%d%d%d",&v1[i],&v2[i],&ord[i]);
			ord[i]++;
		}
		for(i=n;i>=1;i--)
		{
			aim[i]=query(ord[i],1,n,1);//计算最终位置
			ex[aim[i]]=i;
		}
		memset(cnt,0,sizeof cnt);//第二棵树
		for(i=1;i<=n;i++)
		{
			pos=query2(ord[i]-1,1,n,1);//查找前面
			if(pos==-1)printf("-1 ");
			else printf("%d ",v1[ex[pos]]);
			pos=query2(ord[i],1,n,1);//查找后面
			if(pos==-1)printf("-1\n");
			else printf("%d\n",v2[ex[pos]]);
			updata(aim[i],1,n,1);//插入物品
		}
	}
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值