[P1972 [SDOI2009] HH的项链] 树状数组

题目链接:P1972 [SDOI2009]HH的项链

这个题本来是当做莫队算法练手的,结果什么O2优化,快读快输出啥的都搞了,就是T, 然 后 就 把 这 个 莫 队 b a n 了 {\cancel{然后就把这个莫队ban了}} ban ,于是慢慢发现这个算法是个伪算法。还是树状数组为正道解法。

题意

给你一个长度为n可能有重复数字的数组,然后q次查询,问区间[l,r]内不重复的数字有多少种。

题解

本题先分析,我们可以发现当[x,R] (R相同时),前面重复的只取最后一个就行。
以样咧为例:
6
1 2 3 4 3 5
对于[3,5],第三个3完全可以被第五个3代替。
所以我们可以先对R从小到大排序,然后维护一个树状数组,树状数组是干什么用的呢?
假如查询][1,5]
遇到1,没有被标记过,所以add(1,1)。
遇到2,没有被标记过,所以add(2,1)。
遇到3,没有被标记过,所以add(3,1)。
遇到4,没有被标记过,所以add(4,1)。
遇到3,被标记过,此时我们add(3,-1),然后add(5,1)。
这样下来标记数组c就为
a[i]: 1 2 3 4 3
c[i]: 1 1 0 1 1
很容易发现此时查询[l,r]的答案为sum(l)-sum(r-1),sum(i)为c[i]的前缀和。所以我们用树状数组去维护这个c[i]就行。
由于本题的关键点在于将重复的数字只将区间内最后一次出现的标为1,所以只能先存储查询,然后不断扩大r,每扩大一次,在区间内进行add操作和sum求解即可。

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
//extern "C"{void *__dso_handle=0;}
typedef long long ll;
typedef long double ld;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define pii pair
#define lowbit(x) x&-x
const double PI=acos(-1.0);
const double eps=1e-6;
const ll mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxn=1e6+10;
const int maxm=2e6+10;
#define ios ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

int a[maxn],c[maxn],n,ans[maxn],book[maxn];
struct Query{
	int l,r,id;
}query[maxn];

void add(int x,int d)
{
	while(x<=n)
	{
		c[x]+=d;
		x+=lowbit(x);
	}
}
int sum(int x)
{
	int num=0;
	while(x>0)
	{
		num+=c[x];
		x-=lowbit(x);
	}
	return num;
}

bool cmp(Query a, Query b)
{
	return a.r<b.r;
}

//map book;
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int q;
	scanf("%d",&q);
	for(int i=0;i<q;i++)
	{
		scanf("%d%d",&query[i].l,&query[i].r);
		query[i].id=i;
	}
	sort(query,query+q,cmp);
	int next=1;
	for(int i=0;i<q;i++)
	{
		for(int j=next;j<=query[i].r;j++)
		{
			if(book[a[j]]) add(book[a[j]], -1);
			add(j,1);
			book[a[j]]=j;
			next=query[i].r;
		}
		ans[query[i].id]=sum(query[i].r)-sum(query[i].l-1);
	}
	for(int i=0;i<q;i++) printf("%d\n",ans[i]);
}

你可能感兴趣的:(线段树/数状数组)