\(\\\)

\(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. SSM+Shiro

    1) 表名:用户表(Sys_Users) Salt:盐(佐料).为避免被黑客等进行攻击(暴力密码破解),所以一般在注册用户信息时,系统会随机生成一个随机码.在验证时会将密码和随机码进行运算,以验证密码 ...

  2. 【Codeforces 1118D1】Coffee and Coursework (Easy version)

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 从小到大枚举天数. 然后贪心地,从大到小分配a[i]到各个天当中. a[n]分配到第1天,a[n-1]分配到第2天,...然后a[n-x]又分 ...

  3. 你的ExcelUtil简单、高效、易扩展吗

    你的ExcelUtil简单.高效.易扩展吗 Author: Dorae Date: 2018年10月23日12:30:15 转载请注明出处 一.背景 最近接到了和Excel导出相关的需求,但是: 项目 ...

  4. So easy

    Problem Description Small W gets two files. There are n integers in each file. Small W wants to know ...

  5. [bzoj3893][Usaco2014 Dec]Cow Jog_暴力

    Cow Jog bzoj-3893 Usaco-2014 Dec 题目大意:题目链接. 注释:略. 想法: 先按照坐标排序. 我们发现每个牛只会被后面的牛影响. 所以我们考虑逆向枚举. 记录一下i+1 ...

  6. Ubuntu查看隐藏文件夹的方法

    比如要查看当前用户目录下的隐藏文件夹 进入/home/jim目录,使用快捷键Ctrl+H,即可显示隐藏文件夹,如果要关闭,再次按Ctrl+H即可. GUI操作如下所示: 进入文件夹,左上角->查 ...

  7. eventlet

    项目中要模拟openstack的swift的多路write功能.初步设想是用python的eventlet来实现多线程.比如在write的时候要同时写入两个storage,就用eventlet生成2个 ...

  8. 微信企业号开发:corpsecret究竟在哪块呢?

    开发微信企业号,获取ACCESS_TOKEN是必须的,但怎样获取ACCESS_TOKEN呢? 获取access_token=ACCESS_TOKEN" style="text-de ...

  9. Linux自己主动挂载第二块硬盘分区

    改动/etc/fstab文件,如果挂载到/data文件夹.在最后一行加上 /dev/sdb1 /data ext4 defaults 1 1 最后结果例如以下: # # /etc/fstab # Cr ...

  10. zoj 1671 Walking Ant

    Walking Ant Time Limit: 2 Seconds      Memory Limit: 65536 KB Ants are quite diligent. They sometime ...