\(\\\)

\(Description\)


给出一个长为\(N\)的序列,\(M\)次询问区间\([L_i,R_i]\)内不同数字的个数。

  • \(N\in [1,5\times 10^4]\),\(M\in [1,2\times 10^5]\),\(L_i,R_i\in [1,N]\)

\(\\\)

\(Solution\)


  • 离线做法,将询问按右端点从小到大排序。
  • 对下标开树状数组,每次添加数字在该位置\(+1\),在上一次该数字出现的位置\(-1\),然后将能回答询问以前缀和相减的方式都回答了。
  • 关于正确性,如上做法可以看成只为每个颜色保留最后一次出现的标记,因为将询问排序过,所以更新的树状数组所求出的答案一定是对于当前右端点是合法的。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define R register
#define N 500010
#define M 200010
using namespace std; int n,m,clr[N],ans[M],last[1000010]; struct tasks{int l,r,num,ans;}tsk[M]; struct BIT{
int c[N];
BIT(){memset(c,0,sizeof(c));}
inline int lowbit(int x){return x&(-x);}
inline void add(int x,int k){
for(R int i=x;i<=n;i+=lowbit(i)) c[i]+=k;
}
inline int sum(int x){
int res=0;
for(R int i=x;i;i-=lowbit(i)) res+=c[i];
return res;
}
}bit; inline int rd(){
int x=0;
char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)){
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x;
} inline bool cmp(tasks a,tasks b){
return (a.r==b.r)?(a.l<b.l):(a.r<b.r);
} int main(){
n=rd();
for(R int i=1;i<=n;++i) clr[i]=rd();
m=rd();
for(R int i=1;i<=m;++i) {
tsk[i].l=rd();
tsk[i].r=rd();
tsk[i].num=i;
}
sort(tsk+1,tsk+1+m,cmp);
int now=1;
for(R int i=1;i<=n;++i){
bit.add(i,1);
if(last[clr[i]]) bit.add(last[clr[i]],-1);
last[clr[i]]=i;
while(i==tsk[now].r){
tsk[now].ans=bit.sum(i)-bit.sum(tsk[now].l-1);
++now;
}
if(now>m)break;
}
for(R int i=1;i<=m;++i) ans[tsk[i].num]=tsk[i].ans;
for(R int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}

\(\\\)

\(Extend\)


将询问改为,求区间内至少出现过两次的不同数字个数。

\(\\\)

  • 注意到只有询问区间内出现两次数字才是有效的,但仿照之前的写法如果直接标记第二次出现的数,并取消第一次出现的数却会出错,是因为可能所谓的第一次出现的数并不在询问区间里,而所谓的第二个数却被算进了答案。
  • 于是做法就改为记录两次上一个出现的位置,每次出现一个数将两次前出现该数的位置\(-1\),一次前出现的位置\(+1\)即可,这样能保证一个数只算了一次,并且出现两次的数一定打过标记。

\(\\\)

\(Code\)


#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 2000010
#define R register
#define gc getchar
using namespace std; inline int rd(){
int x=0; bool f=0; char c=gc();
while(!isdigit(c)){if(c=='-')f=1;c=gc();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
return f?-x:x;
} int n,c,m,s[N],lst1[N],lst2[N],ans[N]; struct query{int l,r,num;}q[N]; inline bool cmp(query x,query y){
return x.r==y.r?x.l<y.l:x.r<y.r;
} struct BIT{
int c[N];
BIT(){memset(c,0,sizeof(c));}
inline int lowbit(int x){return x&-x;}
inline void add(int p,int x){
for(;p<=n;p+=lowbit(p)) c[p]+=x;
}
inline int sum(int p){
int res=0;
for(;p;p-=lowbit(p)) res+=c[p];
return res;
}
}bit; int main(){
n=rd(); c=rd(); m=rd();
for(R int i=1;i<=n;++i) s[i]=rd();
for(R int i=1;i<=m;++i){
q[i].l=rd(); q[i].r=rd(); q[i].num=i;
}
sort(q+1,q+1+m,cmp);
for(R int i=1,top=1;i<=n;++i){
if(lst2[s[i]]) bit.add(lst2[s[i]],-1);
if(lst1[s[i]]){bit.add(lst1[s[i]],1);}
lst2[s[i]]=lst1[s[i]]; lst1[s[i]]=i;
while(q[top].r==i){
ans[q[top].num]=bit.sum(i)-bit.sum(q[top].l-1);
++top;
}
}
for(R int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}

[ SDOI 2009 ] HH的项链 & [ HEOI 2012 ] 采花的更多相关文章

  1. [BZOJ 2743] [HEOI 2012] 采花

    Description 萧芸斓是Z国的公主,平时的一大爱好是采花.今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 \(n\) 朵花,花有 \(c\) 种颜色(用整数 \ ...

  2. [SDOI 2009]HH的项链

    Description HH有一串由各种漂亮的贝壳组成的项链.HH相信不同的贝 壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义.HH不断地收集新的贝壳,因此,他的项链变得 ...

  3. 解题:HEOI 2012 采花

    题面 题外话:LYD说他当时看错题了,考场爆零了,然后有了作诗这道题=.= 离线处理询问,按右端点递增排序,然后对于每种花$flw[i]$,我们求一个$pre[flw[i]]$表示这种花上一次出现的位 ...

  4. [HEOI 2012] 采花

    [题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2743 [算法] 首先预处理nxt[]数组 , 其中 , nxt[i]表示下一个和i号 ...

  5. SDOI HH的项链 HEOI采花

    题目大意: SDOI求一个区间内只出现一次的数的个数.多组询问. HEOI 求一个区间内出现至少两次的数的个数.多组询问. SDOI HH'neckplace如果每次询问都是1..r的话,那么我们只要 ...

  6. BZOJ 1878 SDOI 2009 HH项链 树状数组 + 脱机处理

    标题效果:一些珠子项链.珠具有不同的颜色.我们问了很多次有多少种不同的颜色有过一段范围. 思考:这个问题让我学会聪明的离线实践.按左端点排序问题.加工出来的位置每种颜色首次出现.每一种颜色的下一次出现 ...

  7. [SDOI 2009]HH去散步

    Description HH有个一成不变的习惯,喜欢饭后百步走.所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离. 但 是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回. 又 ...

  8. [BZOJ 1875] [SDOI 2009] HH去散步【矩阵乘法】

    题目链接:BZOJ - 1875 题目分析: 这道题如果去掉“不会立刻沿着刚刚走来的路走回”的限制,直接用邻接矩阵跑矩阵乘法就可以了.然而现在加了这个限制,建图的方式就要做一些改变.如果我们把每一条边 ...

  9. sdoi 2009 HH去散步 矩阵乘

    如果没有题里的"不会立刻沿着刚刚走来的路走回"限制,那么直接矩乘计算k步的方案数 但加了这个限制,就不能以点来矩乘了,考虑边数<=60,如果以边建邻接矩阵呢?? 先拆边,再把 ...

随机推荐

  1. Abstract factory抽象工厂--对象创建型

    意图: 提供一个创建一系列相关或者相互依赖对象的接口,而无需指定它们具体的类. 别名:Kit 补充: 抽象产品A : (产品A1 和产品 A2) 抽象产品B : ( 产品B1 和 产品B2) 一般情况 ...

  2. Ubuntu 16.04安装Bless十六进制编辑器

    一款专注于十六进制的编辑器. 安装: sudo apt-get install bless 启动:

  3. mysqlhighavailability

    http://mysqlhighavailability.com/getting-started-with-mysql-group-replication/

  4. 跟我一起写Makefile:概述

    什么是makefile?也许非常多Winodws的程序猿都不知道这个东西.由于那些Windows的集成开发环境(integrateddevelopment environment,IDE)都为你做了这 ...

  5. 基于TCP/UDP的socket编程

    基于TCP(面向连接)的socket编程服务器端顺序: 1. 创建套接字(socket) 2. 将套接字绑定到一个本地地址和端口上(bind) 3. 将套接字设为监听模式,准备接收客户请求(liste ...

  6. Codeforces #2B The least round way(DP)

    Description 有一个n*n的正整数矩阵,要你求一条从第一行第一列的格子到第n行第n列的路,使得你走过的格子里面的数乘起来的值末尾的零的个数最小.输出最小个数. Input 第一行包括1个数n ...

  7. Parallel and Perpendicular

    题目链接 题意: 输入n,求正n边形中的对角线1和对角线2的个数(对角线1:至少与其它一个对角线平行:对角线2:至少与其它一个对角线垂直).对角线不能是多边形的边 (4 ≤ n ≤ 10e5) 分析: ...

  8. Silverlight,Windows 8应用开发实例教程系列汇总

    Kevin Fan分享开发经验,记录开发点滴 Silverlight,Windows 8应用开发实例教程系列汇总 2012-06-18 01:05 by jv9, 2145 阅读, 3 评论, 收藏, ...

  9. InfluxDB 分布式时间序列数据库环境搭建——据qcon大会2016qiniu说集群很坑且闭源了

    InfluxDB 分布式时间序列数据库环境搭建   1. 环境说明 Ubuntu14.04  + influxDB V0.10.1 搭建3个节点的分布式数据库,副本数量2,各节点之间自动进行数据备份并 ...

  10. [SCOI 2010] 连续攻击游戏

    [题目链接] https://www.luogu.org/problemnew/show/P1640 [算法] 二分图匹配 实现时需要常数优化和特判 [代码] //code by byf and lm ...