返回顶部

HH的项链

  • 题目描述
    HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。
    HH不断地收集新的贝壳,因此他的项链变得越来越长。
    有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。
    于是,他只好求助睿智的你,来解决这个问题。

  • 输入格式
    第一行:一个整数N,表示项链的长度。
    第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。
    第三行:一个整数M,表示HH询问的个数。
    接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

  • 输出格式
    M行,每行一个整数,依次表示询问对应的答案。

  • 样例输入

    1 2 3 4 3 5
    3
    1 2 
    3 5
    2 6
    
  • 样例输出

    2
    2
    4
    

    这是一道树状数组题,我们可以套用单点修改,区间查询的方式存一个数组C来记录
    当前这个点对应的id是否重复,仔细观察样例,我们会发现如果有区间如1 3 4 3 1
    数组C为1 1 1 0 0
    让你查询[2,3],紧接着[3,4]则会出错为1,正确应为2
    所以我们要不停向右更新数组C的值
    这里要采用离线的思想

  • 离线思想:就是输入完成后,统一输出答案
    这样我们可以给区间L,R排一个序,按R从小到大,这样,我们可以先查R小的,并把R小的中的数更新
    如查询[2,3]可由1 1 1 0 0更新为1 0 1 1 0以此类推,即可保证正确性也可操作少时间短

  • 这里可以用一个数组head存储上一个对应id的位置add(上一个id,-1)相当于置位0(原来为1再减1)
    然后在现在id位置add(id,1)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n,id[50005],m,C[1000005],ans[1000005];
struct ac
{
	int l,r,id;
}a[200005];
bool cmp(ac a,ac b)
{
	return a.r<b.r;
}
//#define int long long 
inline int lowbit(int x)
{
	return (x&(-x));
}
inline int getsum(int x)
{
	int s=0;
	while(x)
	{
		s+=C[x];
		x-=lowbit(x);
	}
	return s;
}
inline void add(int x,int key)
{
	while(x<=n)
	{
		C[x]+=key;
//		C1[i]+=x*key;
		x+=lowbit(x);
	}
}
int head[1000005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&id[i]);
//		if(!ch[id[i]])add(i,1),ch[id[i]]=-1;
	}
	scanf("%d",&m);	
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].id=i;
//		printf("%d\n",getsum(r)-getsum(l-1));		
	}
	sort(a+1,a+1+m,cmp);
//	int np[100005]={};
//	for(int i=n;i>=1;i--)
//	{
//		np[i]=ch[id[i]];
//		ch[id[i]]=i;
//	}
	int l=1;//从前往后找
	for(int i=1;i<=m;i++)	
	{
		while(l<=a[i].r)
		{	
			add(l,1);
            if(head[id[l]]) 
			{
               add(head[id[l]],-1); 
            }
            head[id[l]]=l;
            ++l;			
		}
	    ans[a[i].id] = getsum(a[i].r) - getsum(a[i].l-1);
	}
	for(int i=1;i<=m;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0;
}

2024/4/17Update

为什么不用莫队算法呢?

朴素莫队算法
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int n,id[N],m;
struct ac
{
	int l,r,id;
}a[N];
int cnt[N];
int l=1,r,ans;
void add(int x)
{
	if(cnt[x]==0)ans++;
	cnt[x]++;
}
void del(int x)
{
	cnt[x]--;
	if(cnt[x]==0)ans--;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&id[i]);
	}
	scanf("%d",&m);	
	int x,y;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		while(x<l){l--;add(id[l]);}
		while(x>l){del(id[l]);l++;}
		while(y<r){del(id[r]);r--;}
		while(y>r){r++;add(id[r]);}
		printf("%d\n",ans);
	}

	return 0;
}


image

所以可优化一下

点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int n,id[N],m;
struct ac
{
	int l,r,id;
}a[N];
int cnt[N];
int l=1,r,ans,sq,st[N],en[N],belong[N];
int tot[N];
bool cmp(ac a,ac b)
{
	int p=belong[a.l],q=belong[b.l];
	if(p!=q)return p<q;
	if((p&1)==1)return a.r<b.r;
	else return a.r>b.r;
}
void init()
{
	sq=sqrt(n);
	for(int i=1;i<=sq;i++)
	{
		st[i]=n/sq*(i-1)+1;
		en[i]=n/sq*i;
	}
//	en[sq]=n;
	if(en[sq]<n)
	{
		sq++;
		st[sq]=en[sq-1]+1;
		en[sq]=n;
	}
	for(int i=1;i<=sq;i++)
	{
		for(int j=st[i];j<=en[i];j++)
		{
			belong[j]=i;
		}
	}
}
void add(int x)
{
	if(cnt[x]==0)ans++;
	cnt[x]++;
}
void del(int x)
{
	cnt[x]--;
	if(cnt[x]==0)ans--;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&id[i]);
	}
	scanf("%d",&m);	
	init();
	int x,y;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		a[i]={x,y,i};
	}
	sort(a+1,a+1+m,cmp);
	for(int i=1;i<=m;i++)
	{
		x=a[i].l;
		y=a[i].r;
		while(x<l){l--;add(id[l]);}
		while(x>l){del(id[l]);l++;}
		while(y<r){del(id[r]);r--;}
		while(y>r){r++;add(id[r]);}
		tot[a[i].id]=ans;
	}
	for(int i=1;i<=m;i++)printf("%d\n",tot[i]);
	return 0;
}


posted @ 2024-02-18 22:06  wlesq  阅读(9)  评论(0编辑  收藏  举报