P1972 [SDOI2009]HH的项链
题意:
链接:
https://www.luogu.org/problemnew/show/1972
题解:
很好的模板题。。。。
1.主席树
为避免将一个元素重复计算,我们可以保证只计算一个区间内该元素的最后一个
在操作时我们只需要将这个位置的线段树的这个元素的下一个出现的位置的值++(有点绕)
之后就只需要查询在区间内大于r的元素的个数,这很显然就是一个主席树的板题了
细节:对于每种元素的最后一个可以将其插入在n+1处
代码:
#include <bits/stdc++.h>
using namespace std;
#define maxn 2000000
#define mid (h+t)/2
int f[maxn],a[maxn],pos[maxn],fa[maxn],l;
struct ree{int a,b;}b[maxn];
struct re{int h,t,x;}p[maxn];
void build(int x,int h,int t)
{
l=max(l,x);
if (h==t) return;
p[x].h=x*2; p[x].t=x*2+1;
build(x*2,h,mid); build(x*2+1,mid+1,t);
}
void insert(int x,int &y,int goal,int h,int t)
{
y=++l;
p[y]=p[x]; p[y].x++;
if (h==t) return;
if (goal<=mid) insert(p[x].h,p[y].h,goal,h,mid);
else insert(p[x].t,p[y].t,goal,mid+1,t);
}
int query(int x,int y,int h,int t)
{
if (t<=y) return(0);
if (h>y) return(p[x].x);
return(query(p[x].h,y,h,mid)+query(p[x].t,y,mid+1,t));
}
bool cmp(ree x,ree y)
{
return(x.b<y.b);
}
int main(){
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
int n,m,c,d;
cin>>n;
for (int i=1;i<=n;i++)
b[i].a=i,cin>>b[i].b;
sort(b+1,b+n+1,cmp);
b[0].b=99999999; int o=0;
for (int i=1;i<=n;i++)
{
if (b[i].b!=b[i-1].b) o++;
a[b[i].a]=o;
}
for (int i=n;i>=1;i--)
{
if (f[a[i]]==0) pos[i]=n+1;
else pos[i]=f[a[i]];
f[a[i]]=i;
}
n+=2;
build(1,1,n);
fa[0]=1;
for (int i=1;i<=n;i++)
{
insert(fa[i-1],fa[i],pos[i],1,n);
}
cin>>m;
for (int i=1;i<=m;i++)
{
cin>>c>>d;
cout<<(query(fa[d],d,1,n)-query(fa[c-1],d,1,n))<<endl;
}
}
2.离线+树状数组
对于若干个询问的区间[l,r],如果他们的r都相等的话,那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的
所以我们可以首先将区间按照r排序
这样有什么好处呢,如果一个元素重复出现的话,之前的那个位置上的就再也不可能有用了
所以可以用树状数组来维护,如果一个元素重复出现,sum【现在的位置】++,sum【原先的位置】--;
统计的时候只需要计算sum[i]-sum[j-1]即可
代码:
#include <bits/stdc++.h>
using namespace std;
#define maxn 2000000
int a[maxn],f[maxn],p[maxn],c[maxn],n,m;
struct re{int a,b,c;}b[maxn];
bool cmp(re x,re y)
{
return(x.b<y.b);
}
int lowbit(int x)
{
return(x & -x);
}
void change(int x,int y)
{
while (x<=n)
{
p[x]+=y;
x+=lowbit(x);
}
}
int query(int x)
{
int num=0;
while (x>0)
{
num+=p[x];
x-=lowbit(x);
}
return(num);
}
int main()
{
freopen("noip.in","r",stdin);
freopen("noip3.out","w",stdout);
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i];
cin>>m;
for (int i=1;i<=m;i++)
cin>>b[i].a>>b[i].b,b[i].c=i;
sort(b+1,b+m+1,cmp);
int now=1;
for (int i=1;i<=m;i++)
{
for (int j=now;j<=b[i].b;j++)
{
if (f[a[j]]>0) change(f[a[j]],-1);
change(j,1);
f[a[j]]=j;
}
now=b[i].b+1;
c[b[i].c]=query(b[i].b)-query(b[i].a-1);
}
for (int i=1;i<=m;i++)
cout<<c[i]<<endl;
}
3.莫队
最暴力的区间解法,裸的莫队