BZOJ 1878 【SDOI2009】HH的项链(离线+树状数组)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1878

思路:参考了http://blog.csdn.net/njlcazl/article/details/8758443

下面加一些自己的理解:

因为操作中只有询问没有更新,所以可以使用离线算法,对所有询问按右区间升序排序。

以该数字第一次在区间中出现的点代表所有的点。如果是第一次出现,那么该数字 在之前从未出现或上一次出现不再区间内。

记录每个位置i的数字的前一个相同数字出现的位置pre[i],没有前一个相同的pre[i]为0。

然后从前到后扫描询问,每次将上一个同值点的值加1,然后求当前区间的左界的前缀和就是答案了。将当前位置下个位置的值减1,这样做可以保证任意一个数字在任意一段区间中最多出现一次。

代码中注释位置及下一行+1的原因是树状数组的第一个元素只能从1开始,不能从0开始。

下面给出我的两段代码,其中第二段是将pre[]归算到1开始计数的,避免了树状数组处理时+1的问题,较第一段代码注释处有更改。

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

const int N=1000010;
int n;

struct Node
{
	int id,x,y;
	bool operator < (const Node &b) const
	{
		return y<b.y;
	}
}ask[N];

int last[N],pre[N],ans[N];
int bit[N];

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

void Update (int k,int x)
{
	while (k<=n)
	{
		bit[k]+=x;
		k+=lowbit(k);
	}
}

int Getsum (int k)
{
	int sum=0;
	while (k>0)
	{
		sum+=bit[k];
		k-=lowbit(k);
	}
	return sum;
}

int main ()
{
	int i,q,temp;
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	{
		scanf("%d",&temp);
		pre[i]=last[temp];
		last[temp]=i;
	}
	scanf("%d",&q);
	for (i=0;i<q;++i)
	{
		scanf("%d%d",&ask[i].x,&ask[i].y);
		ask[i].id=i;
	}
	sort(ask,ask+q);
	int now=0;
	for (i=0;i<q;i++)
	{
		while (now<ask[i].y)
		{
			++now;
			Update (pre[now]+1,1);    //树状数组从1开始
			Update (now+1,-1);
		}
		ans[ask[i].id]=Getsum(ask[i].x);
	}
	for (i=0;i<q;i++)
		printf("%d\n",ans[i]);
	return 0;
}

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

const int N=1000010;
int n;

struct Node
{
	int id,x,y;
	bool operator < (const Node &b) const
	{
		return y<b.y;
	}
}ask[N];

int last[N],pre[N],ans[N];
int bit[N];

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

void Update (int k,int x)
{
	while (k<=n+1)    //
	{
		bit[k]+=x;
		k+=lowbit(k);
	}
}

int Getsum (int k)
{
	int sum=0;
	while (k>0)
	{
		sum+=bit[k];
		k-=lowbit(k);
	}
	return sum;
}

int main ()
{
	int i,q,temp;
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	{
		scanf("%d",&temp);
		pre[i]=last[temp]+1;  //
		last[temp]=i;
	}
	scanf("%d",&q);
	for (i=0;i<q;i++)
	{
		scanf("%d%d",&ask[i].x,&ask[i].y);
		ask[i].id=i;
	}
	sort(ask,ask+q);
	int now=1;        //
	for (i=0;i<q;i++)
	{
		while (now<=ask[i].y)
		{
			++now;
			Update (pre[now-1],1);    //
			Update (now,-1);      //
		}
		ans[ask[i].id]=Getsum(ask[i].x);
	}
	for (i=0;i<q;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、付费专栏及课程。

余额充值