1 条题解
-
0
自动搬运
来自洛谷,原作者为

wangjyqh
**搬运于
2025-08-24 21:56:38,当前版本为作者最后更新于2018-09-19 17:12:42,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
提供一个按照右端点排序的思路(类比hh的项链)
回顾hh的项链,我们使用离线+树状数组,和last[j]表示上一次出现j的位置,对于第p个的数j,我们add(last[j],-1),add(p,1),因为我们按照r排序,只用管最右边的一个,所以留下最右端的就可以,对于一个询问直接树状数组求区间和;
比较一下这两道题的区别,同样是计算颜色不同的数量,但是这道题目要求只有存在两个相同颜色这个颜色才做出贡献,怎么办呢?
我们维护两个东西,last1[j]表示上上次出现j的位置,last2[j]表示上次出现的位置
对于第p个的数j
第一次出现j时没有用,我们直接记录last1=p;
第二次出现j时,他就会产生代价,但值得注意的是我们不应该在第二次出现j的为位置上+1,而是在上一次出现的位置last1上+1,这是因为我们按照r从小到大排序,比如2,2,3这个序列,如果我们在第二次出现2的位置上+1,变成(0,1,0),当询问[2,3]就不对了,应该在倒数第二次出现的位置+1变成(1,0,0);
两次以上出现,我们只需要在倒数第2次的位置上+1,其他位置上出现j全部为零,实现:add(last1[j],-1);add(last2[j],1),last1=last2,last2=j;
扩展:如果将题目的限制改一改变成有n个颜色同时在询问区间,我们我们可以仍按照r递增排序,维护last1,last2,~~,lastn;让倒数第n次的位置上+1,其他出现j的位置上全是0
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; struct node { int l,r,pos; }t[2000010]; int last1[2000010],last2[2000010],c[2000010],a[2000010],ans[2000010]; int n,q,m; inline bool cmp(node A,node B){ return A.r<B.r; } inline void add(int x,int val){ for(int i=x;i<=n;i+=(i&-i))c[i]+=val; } inline int query(int x){ int ans=0; for(int i=x;i;i-=(i&-i))ans+=c[i]; return ans; } int main(){ scanf("%d%d%d",&n,&q,&m); for(int i=1;i<=n;++i)scanf("%d",&a[i]); for(int i=1;i<=m;++i){ scanf("%d%d",&t[i].l,&t[i].r); t[i].pos=i; } sort(t+1,t+m+1,cmp); int j=1; for(int i=1;i<=m;++i){ for(;j<=t[i].r;++j){ if(!last1[a[j]])last1[a[j]]=j; else { if(!last2[a[j]]){ add(last1[a[j]],1); last2[a[j]]=j; }else { add(last2[a[j]],1); add(last1[a[j]],-1); last1[a[j]]=last2[a[j]]; last2[a[j]]=j; } } } ans[t[i].pos]=query(t[i].r)-query(t[i].l-1); } for(int i=1;i<=m;++i)printf("%d\n",ans[i]); return 0; }
- 1
信息
- ID
- 3051
- 时间
- 2000ms
- 内存
- 500MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者