HDU - 4417 Super Mario(主席树/线段树+离线)

题目链接:点击查看

题目大意:给出由 n 个数的数列,再给出 m 次查询,每次查询需要输出 [ l , r ] 内小于等于 h 的数有多少个

题目分析:大晚上睡不着觉随便做做题,发现这个题目原来可以用主席树来做,又发现这个题目去年暑假竟然没写博客,于是补上一发

线段树离线的做法就是对数列和询问的高度排序,遍历每个询问,双指针将小于等于当前询问高度的位置都扔到线段树中,记录当前询问的区间内有多少个数即可

主席树在线做法,有两种,先说一下网上最常见的,就是遍历每个位置作为下标,对于下标维护可持久化线段树,每个权值线段树维护的是高度信息,记录一下每个高度出现了多少次,答案显然就是第 R 个版本中 [ 1 , h ] 的个数减去第 L - 1 个版本中 [ 1 , h ] 的个数了

再说一下我自己的做法,我是对于高度维护了可持久化线段树,每个权值线段树维护的是区间信息,记录一下 [ 1 , h ] 内的所有数在区间上的分布情况,这样答案就是第 h 个版本的权值线段树中 [ l , r ] 中有多少个数了

两种主席树的做法时空复杂度相同且都是需要进行离散化的,但我个人感觉自己的做法更好理解一些,更能体现了:主席树是可持久化线段树,其中每个版本的主席树单独拿出来都是一个权值线段树

代码:

主席树:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e5+100;

struct Node
{
	int l,r;
	int sum;
}tree[N*20];

int cnt,root[N];

void update(int num,int &k,int l,int r)
{
	tree[cnt++]=tree[k];
	k=cnt-1;
	tree[k].sum++;
	if(l==r)
		return;
	int mid=l+r>>1;
	if(num<=mid)
		update(num,tree[k].l,l,mid);
	else
		update(num,tree[k].r,mid+1,r);
}

int query(int k,int l,int r,int L,int R)//[l,r]:目标区间,[L,R]:当前区间 
{
	if(Rr)
		return 0;
	if(L>=l&&R<=r)
		return tree[k].sum;
	int mid=L+R>>1;
	return query(tree[k].l,l,r,L,mid)+query(tree[k].r,l,r,mid+1,R); 
}

map>mp;

vectornode;

int get_id(int x)
{
	return upper_bound(node.begin(),node.end(),x)-node.begin();
}

void discreate()
{
	sort(node.begin(),node.end());
	node.erase(unique(node.begin(),node.end()),node.end());
}

void init()
{
	mp.clear();
	node.clear();
	root[0]=0;
	tree[0].l=tree[0].r=tree[0].sum=0;
	cnt=1;
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int w;
	cin>>w;
	int kase=0;
	while(w--)
	{
		init();
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			int h;
			scanf("%d",&h);
			mp[h].push_back(i);
			node.push_back(h);
		}
		discreate();
		for(int i=1;i<=node.size();i++)//按照高度建主席树,每个高度的前缀和的权值线段树代表的是[1,h]内的高度在[1,n]上的分布情况 
		{
			root[i]=root[i-1];
			for(int pos:mp[node[i-1]])
				update(pos,root[i],1,n);
		}
		printf("Case %d:\n",++kase);
		while(m--)
		{
			int l,r,h;
			scanf("%d%d%d",&l,&r,&h);
			l++,r++;
			printf("%d\n",query(root[get_id(h)],l,r,1,n));
		}
	}
















    return 0;
}

离线+线段树:

#include
#include 
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;

const int inf=0x3f3f3f3f;

const int N=1e5+100;

struct Tree
{
	int l,r,sum;
}tree[N<<2];

struct Node
{
	int hei,pos;
	bool operator<(Node a)const
	{
		return hei>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
}

void update(int k,int pos)
{
	if(tree[k].l==tree[k].r)
	{
		tree[k].sum=1;
		return;
	}
	int mid=(tree[k].l+tree[k].r)>>1;
	if(mid>=pos)
		update(k<<1,pos);
	else
		update(k<<1|1,pos);
	tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
}

int query(int k,int l,int r)
{
	if(tree[k].rr)
		return 0;
	if(tree[k].l>=l&&tree[k].r<=r)
		return tree[k].sum;
	return query(k<<1,l,r)+query(k<<1|1,l,r);	
}

int main()
{
//	freopen("input.txt","r",stdin);
	int w;
	cin>>w;
	int kase=0;
	while(w--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i].hei);
			a[i].pos=i;
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%d",&b[i].l,&b[i].r,&b[i].hei);
			b[i].l++;
			b[i].r++;
			b[i].id=i;
		}
		build(1,1,n);
		sort(a+1,a+1+n);
		sort(b+1,b+1+m);
		int pos=1;
		for(int i=1;i<=m;i++)
		{
			int h=b[i].hei;
			while(a[pos].hei<=h)
			{
				update(1,a[pos].pos);
				pos++;
			}
			ans[b[i].id]=query(1,b[i].l,b[i].r);
		}
		printf("Case %d:\n",++kase);
		for(int i=1;i<=m;i++)
			cout<

 

你可能感兴趣的:(线段树,离线处理,主席树)