HDU 4638 Group

主要借鉴这一篇:http://www.2cto.com/kf/201308/233187.html

思路:如果我们从左到右一个个加进来,那么对于加进来的第i个数num[i],那么它就能增加一个段(num[i]+1,和num[i]-1在之前都没出现过),或者减少一个段(num[i]+1,和num[i]-1在之前都出现过),或者不增不减(num[i]+1,和num[i]-1在之前只出现过其中一个)。那么我们要找的询问的区间[l,r]内有多少个区间,就是询问[l,r]内的数,增加了几个这样的段。但是直接询问[l,r]内的数产生的段数是不可以的,因为对于[l,r]内的某一个数,有可能它所能产生的段数是跟l之前,或者r之后的数有关的。因此我们可以将询问离线出来,按r排好序,每次询问之前,将r之前的数产生的影响先清除掉,也就是对于某个在r之前的数,将它所能影响的后面的数能增加的段去除,对于之前的已经不用管了,因为我们按右端点排序,所以在清除某一个数时,它前面的数的影响之前已经处理掉了。所以这个区间的段数自然就是[l,r]的区间和。

线段树和树状数组都能做,这里附上两份代码:

线段树:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define mod 1000000007

struct node
{
	int l , r , val;
}tree[M*4] , tp[M];
int num[M] , pos[M] , ans[M];
bool vis[M];

bool cmp(node a , node b)
{
	if (a.r < b.r)return true;
	return false;
}

void Build(int l , int r , int rt)
{
	tree[rt].l = l;
	tree[rt].r = r;
	tree[rt].val = 0;
	if (l == r)return;
	int mid = (l+r)>>1;
	Build(l,mid,rt<<1);
	Build(mid+1,r,rt<<1|1);
}

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

int Query(int rt , int l , int r)
{
	if (l == tree[rt].l && r == tree[rt].r)return tree[rt].val;
	int mid = (tree[rt].l+tree[rt].r)>>1;
	if (r <= mid)
		return Query(rt<<1,l,r);
	if (l > mid)
		return Query(rt<<1|1,l,r);
	return Query(rt<<1,l,mid)+Query(rt<<1|1,mid+1,r);
}

int main()
{
	int n , m , i , t;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		memset(vis , 0 , sizeof vis);
		for (i = 1 ; i <= n ; i++)
		{
			scanf("%d",num+i);
			pos[num[i]] = i;
		}
		for (i = 1 ; i <= m ; i++)
		{
			scanf("%d%d",&tp[i].l,&tp[i].r);
			tp[i].val = i;
		}
		sort(tp+1 , tp+m+1 , cmp);
		Build(1,n,1);
		int cnt = 1;
		ans[1] = ans[2] = -5;
		for (i = 1 ; i <= n ; i++)
		{
			Updata(1,i,1);
			vis[num[i]] = 1;
			if (vis[num[i]+1])Updata(1,pos[num[i]+1],-1);
			if (vis[num[i]-1])Updata(1,pos[num[i]-1],-1);
			while (i == tp[cnt].r && cnt <= m)
			{
				ans[tp[cnt].val] = Query(1,tp[cnt].l,tp[cnt].r);
				cnt++;
			}
		}
		for (i = 1 ; i <= m ; i++)printf("%d\n",ans[i]);
	}
	return 0;
}

 

树状数组:

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define mod 1000000007

struct node
{
	int l , r , val;
}tp[M];
int n , m , num[M] , pos[M] , ans[M] , sum[M];
bool vis[M];

bool cmp(node a , node b)
{
	if (a.r < b.r)return true;
	return false;
}

int Lowbit(int x)
{
	return x&(-x);
}

void Updata(int pos , int val)
{
	while (pos <= n)
	{
		sum[pos] += val;
		pos += Lowbit(pos);
	}
}

int Query(int l , int r)
{
	int ret = 0;
	while (r > 0)
	{
		ret += sum[r];
		r -= Lowbit(r);
	}
	l--;
	while (l > 0)
	{
		ret -= sum[l];
		l -= Lowbit(l);
	}
	return ret;
}

int main()
{
	int i , t;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		memset(vis , 0 , sizeof vis);
		memset(sum , 0 , sizeof sum);
		for (i = 1 ; i <= n ; i++)
		{
			scanf("%d",num+i);
			pos[num[i]] = i;
		}
		for (i = 1 ; i <= m ; i++)
		{
			scanf("%d%d",&tp[i].l,&tp[i].r);
			tp[i].val = i;
		}
		sort(tp+1 , tp+m+1 , cmp);
		int cnt = 1;
		for (i = 1 ; i <= n ; i++)
		{
			Updata(i,1);
			vis[num[i]] = 1;
			if (vis[num[i]+1])Updata(pos[num[i]+1],-1);
			if (vis[num[i]-1])Updata(pos[num[i]-1],-1);
			while (i == tp[cnt].r && cnt <= m)
			{
				ans[tp[cnt].val] = Query(tp[cnt].l,tp[cnt].r);
				cnt++;
			}
		}
		for (i = 1 ; i <= m ; i++)printf("%d\n",ans[i]);
	}
	return 0;
}



 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值