本人第一篇博客重磅推出!!!

希望各位朋友以后多多捧场也多给写意见(我个人喜欢把题解写得啰嗦一点,因为这样方便理解,各位巨佬勿喷)

来讲一道提高+/省选-的骚题:HH的项链(这个HH你理解成皇后呵呵哈哈嘿嘿花花红红还是蛤蛤都可以)

题目来源:洛谷 P1972 [SDOI2009]HH的项链

题面如下:

题目描述

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答……因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入输出格式

输入格式:

第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:

M 行,每行一个整数,依次表示询问对应的答案。

数据范围:

对于100%的数据,n<=500000,m<=200000


题解部分:

(注:以下内容中的点表示的就是贝壳)

题意分析:区间统计点的种类数

看到这题的数据范围应该就可以判断出暴力统计是不可行的,因为那样时间复杂度将会是O(mn)肯定会超时

而想要快速得区间统计很自然得就会想到线段树、树状数组什么之类的

但是比较恶心的是这道题的题意是要统计种类数,不过可以想到把第一次出现的种类的点(贝壳)的值赋为1,否则赋为0,这样统计种类只要区间求和即可

但是每个点在询问的区间不同是会提供不同的结果,这可要命了QAQ

所以我们需要稍稍改一下思路,看到它是区间询问,就可以联想到可以把L作为第一关键字,R作为第二关键字把询问进行从小到大的排序(即先按L从小到大排序,L相同时按R从小到大排序),这和莫队有些类似,需要离线实现(也就是在读入完所有询问再处理而不是读入一个处理一个)

这样就会方便我们修改点的值,因为现在保证询问的区间左端点是递增的,所以每次当前询问的左端点L1移动到排完序的下一个询问的左端点L2时(此时L2>=L1肯定成立),从第L1个点一直跑到第(L2-1)个点(对,第L2个不能包括,因为它在新询问的区间范围之内),很显然这些点在之后的询问中不会再访问到,所以这些点中如果有是第一个该种类的点(也就是这个点点值是1),那么把下一个与它种类相同的点的点值(改之前肯定是0)改为1,(这里可优化,见下文优化①)因为这个新的点在新的询问的区间中就会是第一个该种类的。

然后用支持快速区间求和算法搞定(我个人比较喜欢尽量用树状数组,因为好写),顺便说一下,由于树状数组的区间求和是类似于求两个前缀和相减得到的,所以虽然在每次左端点移动时两个左端点间每次找到的点值为1的点地位已经不是该种类第一个点了,因为之后询问的区间内根本不会有它,可正是这样,它的点值其实不用改,它的点值在前缀和相减时会被抵消掉,影响不到之后的结果

实现是这样的,先开一个数组a,a[i]表示第i个贝壳的种类,还有数组b存点值,接着是数组t,t[i]存的是种类为i的贝壳当前最后一次出现的位置,用于搞next(后面会讲),初值为0,然后是数组c是维护数组b的树状数组,再开一个next数组,next[i]表示下一个种类与位置在i的贝壳相同的贝壳的位置,初值为500001(即n的最大值+1),这样如果在两个左端点间找到一个贝壳是该种类的最后一个时,就会把第500001个点的点值改为1(这个点其实不存在所以不用担心),在输入时处理一个新贝壳(设位置为i,种类为x),如果t[x]为0,那么这个贝壳就是它的种类的第一个,所以就b[i]=1;t[x]=i;如果t[i]不为0,就next[t[x]]=i;t[x]=i;然后还要开一个结构体用于存储询问,可以这样:

struct ask{

int l;//左端点

int r;//右端点

int address;//询问的位置,因为在排完序后询问的顺序就与原来不同了,所以要把询问输入时的顺序存下来

}hh[200003];

还需要一个ans数组存结果,在每次算完排完序后的第i个询问时就存储在ans[hh[i].address]里,输出结果时正序输出ans数组即可

然后写一个眉清目秀的树状数组,再按上文的意思写就行

优化①:再讲一个优化,在每次左端点移动时两个左端点(L1,L2,且L1<=L2)间找到找到的点值为1的点,即将把下一个与该点同种类的点的点值改为1时,在寻找要修改的点时可以优化,因为如果我们找到的要修改的点也在两个左端点之间,那么它肯定还会被访问到并且再继续修改下一个,有点浪费时间,不如我们这样,如果寻找到的这个点所在的位置小于L2,那么我们就不要改这个点,直接继续找下一个同种类的点,直到找到的点的位置大于等于L2为止(特殊情况就是当找到了一个是它的种类的最后一个的贝壳时,下次就会找到“第500001个点”,它的位置自然会大于L2,也一样可以停止),然后再修改点值,可以用一个while循环实现:

for(int j=hh[i].l;j<=hh[i+1].l-1;j++){

  if(b[j]==1){

  int p=j;

  while(j<hh[i+1].l)

       p=next[p];

  b[p]=1;

  add(p,1);

  }

}

好了,该讲的讲完了,再贴一下我的代码:

#include<bits/stdc++.h>
#define rep(i,n) for(register IL i=1;i<=n;i++)
#define IL int //本题需要的数据类型
using namespace std;
const IL M=500003;
IL n,m,a[M],b[M],c[M],t[1000001],x,limit,ans[200001],next[M];
struct ask{
int l,r,address;
}hh[200001];
bool cmp(ask a,ask b){
return (a.l<b.l||(a.l==b.l&&a.r<b.r));
}
IL lowbit(IL x){//返回x在二进制下的1的最低位
return x&-x;
}
IL add(IL k,IL delt){//在位置k上加上delt
while(k<=limit){
c[k]+=delt;
k+=lowbit(k);
}
}
IL getsum(IL k){//求编号1到k的点的值的和(相当于前缀和)
IL total=0;
while(k>0){
total+=c[k];
k-=lowbit(k);
}
return total;
}
int main(){
scanf("%d",&n);
limit=n;
rep(i,n)
{
next[i]=500001;
scanf("%d",&x);
if(!t[x]){
b[i]=1;
add(i,1);
}
else
next[t[x]]=i;
t[x]=i;
}
scanf("%d",&m);
rep(i,m){
scanf("%d %d",&hh[i].l,&hh[i].r);
hh[i].address=i;
}
sort(hh+1,hh+m+1,cmp);
rep(i,m-1){
ans[hh[i].address]=getsum(hh[i].r)-getsum(hh[i].l-1);
for(int j=hh[i].l;j<=hh[i+1].l-1;j++){
if(b[j]==1){
int p=j;
while(p<hh[i+1].l){
p=next[p];
}
b[p]=1;
add(p,1);
}
}
}
ans[hh[m].address]=getsum(hh[m].r)-getsum(hh[m].l-1);
rep(i,m)
printf("%d\n",ans[i]);
return 0;
}

希望各位好好消化一下,这道题其实挺综合的,很适合练习

HH的项链题解(离线思想+链表+树状数组)的更多相关文章

  1. 【题解】Music Festival(树状数组优化dp)

    [题解]Music Festival(树状数组优化dp) Gym - 101908F 题意:有\(n\)种节目,每种节目有起始时间和结束时间和权值.同一时刻只能看一个节目(边界不算),在所有种类都看过 ...

  2. 【Luogu】P1972HH的项链(链表+树状数组)

    题目链接 难题,所以会讲得细一些. 首先我们想如何统计区间[l,r]内不同贝壳的个数. 第一个思路就是线段树/树状数组,query(1,r)-query(1,l-1)对不对? 然而这样是不对的. 然后 ...

  3. 【BZOJ4548】小奇的糖果 set(链表)+树状数组

    [BZOJ4548]小奇的糖果 Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的 ...

  4. HDU 4602 Magic Ball Game(离线处理,树状数组,dfs)

    Magic Ball Game Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) ...

  5. 【BZOJ3295】【块状链表+树状数组】动态逆序对

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  6. Bzoj4548 小奇的糖果(链表+树状数组)

    题面 Bzoj 题解 很显然,我们只需要考虑单独取线段上方的情况,对于下方的把坐标取反再做一遍即可(因为我们只关心最终的答案) 建立树状数组维护一个横坐标区间内有多少个点,维护双向链表实现查询一个点左 ...

  7. PAT甲级题解-1057. Stack (30)-树状数组

    不懂树状数组的童鞋,正好可以通过这道题学习一下树状数组~~百度有很多教程的,我就不赘述了 题意:有三种操作,分别是1.Push key:将key压入stack2.Pop:将栈顶元素取出栈3.PeekM ...

  8. noi.ac #44 链表+树状数组+思维

    \(des\) 给出长度为 \(n\) 的序列,全局变量 \(t\),\(m\) 次询问,询问区间 \([l, r]\) 内出现次数为 \(t\) 的数的个数 \(sol\) 弱化问题:求区间 \([ ...

  9. 洛谷 P2163 [SHOI2007]园丁的烦恼 (离线sort,树状数组,解决三维偏序问题)

    P2163 [SHOI2007]园丁的烦恼 题目描述 很久很久以前,在遥远的大陆上有一个美丽的国家.统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草. 有一天国王漫步在花园 ...

随机推荐

  1. mybatis深入理解(一)之 # 与 $ 区别以及 sql 预编译

    mybatis 中使用 sqlMap 进行 sql 查询时,经常需要动态传递参数,例如我们需要根据用户的姓名来筛选用户时,sql 如下: select * from user where name = ...

  2. 【aspnetcore】配置使用jwt验证

    因为害怕token泄露出现问题,所以从未在项目中使用jwt.但这玩意现在真的很火,趁有空还是研究了一下. 在aspnetcore中实现jwt很简单,感觉微软把很多工作都做了,虽然开发效率上去了,但是使 ...

  3. 才知道 Windows Live Writer Source Code plugin for SyntaxHighlighter 更新到2.0了

    这是我用 Windows Live Writer 发布的第一篇文章! 在官方网站看到 Windows Live Writer Source Code plugin for SyntaxHighligh ...

  4. jquery——制作置顶菜单

    置顶菜单: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  5. Solve Equations HackerRank 扩展欧几里德 && 数学

    https://www.hackerrank.com/contests/infinitum16-firsttimer/challenges/solve-equations 给定一条方程a*x + b* ...

  6. Hadoop TaskScheduler源码分析

    TaskScheduler是MapReduce中的任务调度器.在MapReduce中,JobTracker接收JobClient提交的Job,将它们按InputFormat的划分以及其他相关配置,生成 ...

  7. SpringBoot源码篇:深度分析SpringBoot如何省去web.xml

    一.前言 从本博文开始,正式开启Spring及SpringBoot源码分析之旅.这可能是一个漫长的过程,因为本人之前阅读源码都是很片面的,对Spring源码没有一个系统的认识.从本文开始我会持续更新, ...

  8. 【Linux】linux下tar.gz、tar、bz2、zip等解压缩、压缩命令小结

    Linux下最常用的打包程序就是tar了,使用tar程序打出来的包我们常称为tar包,tar包文件的命令通常都是以.tar结尾的.生成tar包后,就可以用其它的程序来进 行压缩了,所以首先就来讲讲ta ...

  9. Json数组对象和对象数组

    Json的简单介绍 从结构上看,所有的数据最终都可以分成三种类型: 第一种类型是scalar(标量),也就是一个单独的string(字符串)或数字(numbers),比如“北京”这个单独的词. 第二种 ...

  10. 在Azure上搭架***代理服务器

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:在Azure上搭架***代理服务器.