HH的项链题解(离线思想+链表+树状数组)
本人第一篇博客重磅推出!!!
希望各位朋友以后多多捧场也多给写意见(我个人喜欢把题解写得啰嗦一点,因为这样方便理解,各位巨佬勿喷)
来讲一道提高+/省选-的骚题: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的项链题解(离线思想+链表+树状数组)的更多相关文章
- 【题解】Music Festival(树状数组优化dp)
[题解]Music Festival(树状数组优化dp) Gym - 101908F 题意:有\(n\)种节目,每种节目有起始时间和结束时间和权值.同一时刻只能看一个节目(边界不算),在所有种类都看过 ...
- 【Luogu】P1972HH的项链(链表+树状数组)
题目链接 难题,所以会讲得细一些. 首先我们想如何统计区间[l,r]内不同贝壳的个数. 第一个思路就是线段树/树状数组,query(1,r)-query(1,l-1)对不对? 然而这样是不对的. 然后 ...
- 【BZOJ4548】小奇的糖果 set(链表)+树状数组
[BZOJ4548]小奇的糖果 Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的 ...
- HDU 4602 Magic Ball Game(离线处理,树状数组,dfs)
Magic Ball Game Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) ...
- 【BZOJ3295】【块状链表+树状数组】动态逆序对
Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...
- Bzoj4548 小奇的糖果(链表+树状数组)
题面 Bzoj 题解 很显然,我们只需要考虑单独取线段上方的情况,对于下方的把坐标取反再做一遍即可(因为我们只关心最终的答案) 建立树状数组维护一个横坐标区间内有多少个点,维护双向链表实现查询一个点左 ...
- PAT甲级题解-1057. Stack (30)-树状数组
不懂树状数组的童鞋,正好可以通过这道题学习一下树状数组~~百度有很多教程的,我就不赘述了 题意:有三种操作,分别是1.Push key:将key压入stack2.Pop:将栈顶元素取出栈3.PeekM ...
- noi.ac #44 链表+树状数组+思维
\(des\) 给出长度为 \(n\) 的序列,全局变量 \(t\),\(m\) 次询问,询问区间 \([l, r]\) 内出现次数为 \(t\) 的数的个数 \(sol\) 弱化问题:求区间 \([ ...
- 洛谷 P2163 [SHOI2007]园丁的烦恼 (离线sort,树状数组,解决三维偏序问题)
P2163 [SHOI2007]园丁的烦恼 题目描述 很久很久以前,在遥远的大陆上有一个美丽的国家.统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草. 有一天国王漫步在花园 ...
随机推荐
- JS滑动下划线导航菜单实现原理
效果如下:http://campus.51job.com/test/zengxl/js html: <div class="mainnav"> <div clas ...
- POJ1023 The Fun Number System
题目来源:http://poj.org/problem?id=1023 题目大意: 有一种有趣的数字系统.类似于我们熟知的二进制,区别是每一位的权重有正有负.(低位至高位编号0->k,第i位的权 ...
- 洛谷P4891 序列 || 膜法阵,魔法阵
https://www.luogu.org/problemnew/show/P4891 一道几乎一样的题http://210.33.19.103/contest/1130/problem/3 题面ht ...
- CMD当前代码页修改
python3.x在程序开发中统一的编码是 UTF-8,但是进行交互式编程的时候会经常遇到乱码问题,这是因为Window cmd的默认编码是GBK.与程序采用的 UTF-8 不一致造成的中文及特殊字符 ...
- 记录一个在线压缩和还原压缩js代码的工具
packer – javascript 压缩工具 http://dean.edwards.name/packer/ Javascript Beautifier ---可以恢复某些压缩工具压缩的js代码 ...
- 在Pycharm中写python代码时光标变粗
在练习写python代码时,不小心摁了Insert键,结果光标变粗. 如下图所示: 原因: 和word一样,在编辑文本或代码时,有两种模式:改写和插入模式. 当我们在编辑文章或者是代码时,应该将模式设 ...
- pat1099. Build A Binary Search Tree (30)
1099. Build A Binary Search Tree (30) 时间限制 100 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN ...
- 精心整理的SQL语句学习大全
-语 句 功 能 --数据操作SELECT --从数据库表中检索数据行和列INSERT --向数据库表添加新数据行DELETE --从数据库表中删除数据行UPDATE --更新数据库表中的数据-数据 ...
- idea没有绑远程地址,如何提交到github的空项目
一 有同事问我怎么提交到github的空项目....这么简单.... 二 1.创建本地的git仓库 选择自己的项目 创建成功 2.添加代码,选中左边的文件,add 3.提交 5.定义远程仓库,不定义的 ...
- 关于Mdi窗口-父窗口上的控件把子窗口的挡住的问题
using System.Runtime.InteropServices; [DllImport("user32")] public static extern int SetPa ...