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;
}
所以可优化一下
点击查看代码
#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;
}