\(\\\)

\(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. 【05】JSON笔记

    [05]笔记           尽管有许多宣传关于 XML 如何拥有跨平台,跨语言的优势,然而,除非应用于 Web Services,否则,在普通的 Web 应用中,开发者经常为 XML 的解析伤透 ...

  2. 我安装android studio的过程与经历

    虽然android studio已经出来两年多了,但是我一直都没真正用过.之前用Eclipse还算用得挺好.我并不是一个专职的android开发者,我是个游戏开发者,打包的时候要用到android.不 ...

  3. CodeForcesGym 100753E Change of Scenery

    Change of Scenery Time Limit: 10000ms Memory Limit: 262144KB This problem will be judged on CodeForc ...

  4. [luoguP2858] [USACO06FEB]奶牛零食Treats for the Cows(DP)

    传送门 f[i][j][k] 表示 左右两段取到 i .... j 时,取 k 次的最优解 可以优化 k 其实等于 n - j + i 则 f[i][j] = max(f[i + 1][j] + a[ ...

  5. Codeforces Round #244 (Div. 2)

    今天是水题集啊.... A. Police Recruits time limit per test 1 second memory limit per test 256 megabytes inpu ...

  6. 4种OSS的应用架构及核心技术

        基础型 架构描述:OSS作为文件存储源,用户上传下载数据均经过ECS与OSS通信. 解决用户问题:文件空间大,ECS磁盘存储空间有限:多ECS间无法同步数据. 适用场景描述:文件较多,但文件调 ...

  7. 条款45: 弄清C++在幕后为你所写、所调用的函数

    如果你没有声明下列函数,体贴的编译器会声明它自己的版本.这些函数是:一个拷贝构造函数,一个赋值运算符,一个析构函数,一对取址运算符.另外,如果你没有声明任何构造函数,它也将为你声明一个缺省构造函数.所 ...

  8. MVC路由中特殊URL匹配规则

    *匹配*用来匹配URL剩余部分 贪婪匹配规则贪婪匹配会找到最后一个符合条件的“字面量”为止

  9. 阿牛的EOF牛肉串-记忆化搜索或动态规划

    C - 阿牛的EOF牛肉串 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submi ...

  10. input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has

    input屏蔽历史记录   设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处   ;(function($){$.ex ...