P1972 [SDOI2009] HH的项链 题解
P1972 [SDOI2009] HH的项链 题解
补一个扫描线题解。
解法
一句话题意:给定一个序列,多次询问区间
我们设序列中
现在问题即为:给定一个序列
我们把每一个
注意到这个询问是可差分的,我们可以将询问差分为左下角为
我们将所有操作按横坐标排序(包括加点操作和查询操作),建立权值线段树,维护每个纵坐标出现个数。从左向右扫,如果遇到加点操作就在其纵坐标相应的线段树位置上加
单次操作复杂度
代码
#include<bits/stdc++.h>
namespace fast_IO
{
/**
* 快读快写
*/
};
using namespace fast_IO;
int n,m,a[1000010],pre[1000010],lst[1000010],ans[1000010],tot;
struct ope
{
int type,x,y,id;
inline bool operator<(const ope &rhs) const
{
if(x==rhs.x) return type<rhs.type;
return x<rhs.x;
}
};
ope op[3000010];
struct node
{
int cnt;
node *lc,*rc;
inline node()
{
cnt=0,lc=rc=nullptr;
}
inline void pushup()
{
cnt=lc->cnt+rc->cnt;
}
};
class seg_tree
{
#define ls l,mid
#define rs mid+1,r
private:
node *root;
inline node *build(int l,int r)
{
node *rt=new node();
if(l<r)
{
int mid=(l+r)/2;
rt->lc=build(ls),rt->rc=build(rs);
}
return rt;
}
inline void fix(node *rt,const int pos,int l,int r)
{
if(l==r)
{
rt->cnt++;
return;
}
int mid=(l+r)/2;
if(pos<=mid) fix(rt->lc,pos,ls);
else fix(rt->rc,pos,rs);
rt->pushup();
}
inline int ask(node *rt,const int L,const int R,int l,int r)
{
if(L<=l && r<=R) return rt->cnt;
int mid=(l+r)/2,ret=0;
if(L<=mid) ret+=ask(rt->lc,L,R,ls);
if(R>mid) ret+=ask(rt->rc,L,R,rs);
return ret;
}
public:
inline void build()
{
root=build(0,n);
}
inline void fix(const int pos)
{
fix(root,pos,0,n);
}
inline int ask(const int L,const int R)
{
return ask(root,L,R,0,n);
}
};
seg_tree tree;
int main()
{
in>>n,tree.build();
for(int i=1;i<=n;i++) in>>a[i],pre[i]=lst[a[i]],lst[a[i]]=i,op[++tot]=(ope){0,i,pre[i],i};
in>>m;
for(int i=1,l,r;i<=m;i++) in>>l>>r,op[++tot]=(ope){1,r,l-1,i},op[++tot]=(ope){2,l-1,l-1,i};
std::sort(op+1,op+tot+1);
for(int i=1;i<=tot;i++)
{
if(op[i].type==0) tree.fix(op[i].y);
else if(op[i].type==1) ans[op[i].id]+=tree.ask(0,op[i].y);
else ans[op[i].id]-=tree.ask(0,op[i].y);
}
for(int i=1;i<=m;i++) out<<ans[i]<<'\n';
fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
return 0;
}