SPOJ DQUERY - D-query (莫队算法|主席树|离线树状数组)
DQUERY - D-query
Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the number of distinct elements in the subsequence ai, ai+1, ..., aj.
Input
- Line 1: n (1 ≤ n ≤ 30000).
- Line 2: n numbers a1, a2, ..., an (1 ≤ ai ≤ 106).
- Line 3: q (1 ≤ q ≤ 200000), the number of d-queries.
- In the next q lines, each line contains 2 numbers i, j representing a d-query (1 ≤ i ≤ j ≤ n).
Output
- For each d-query (i, j), print the number of distinct elements in the subsequence ai, ai+1, ..., aj in a single line.
Example
Input
5
1 1 2 1 3
3
1 5
2 4
3 5 Output
3
2
3 题意:给你一段区间,q个询问,询问区间[l,r]有多少个不同的数字
思路: 这道题算是很有意思的一道数据结构题了。。解法很多:莫队,主席树,离线树状数组都可以。解法也不难。 莫队解法:
思路:这道题感觉就是直接莫队莽上就完事了,没有什么需要处理的,就维护下每个元素在当前区间出现的次数就好了。
实现代码:
#include<bits/stdc++.h>
using namespace std; const int M = 1e6 + ;
int a[M],num[M],vis[M],block,ans = ,n,m;
struct node{
int l,r,id;
bool operator < (const node &cmp) const{
if(l/block == cmp.l/block) return r < cmp.r;
return l/block < cmp.l/block;
}
}q[M]; void add(int x){
vis[a[x]]++;
if(vis[a[x]] == ) ans++;
} void del(int x){
vis[a[x]]--;
if(vis[a[x]] == ) ans--;
} int main()
{
scanf("%d",&n);
block = sqrt(n);
for(int i = ;i <= n;i ++){
scanf("%d",&a[i]);
}
scanf("%d",&m);
for(int i = ;i <= m;i ++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id = i;
}
sort(q+,q+m+);
int l = ,r = ;
for(int i = ;i <= m;i ++){
while(l < q[i].l) del(l),l++;
while(l > q[i].l) l--,add(l);
while(r < q[i].r) r++,add(r);
while(r > q[i].r) del(r),r--;
num[q[i].id] = ans;
}
for(int i = ;i <= m;i ++)
printf("%d\n",num[i]);
return ; }
ps:
使用莫队算法耗时为:270ms;
主席树解法:
我们将元素在序列中的地址建成主席树,如果这个元素没有出现过,那么就在这个位置上+1,如果这个元素出现过那就将这个元素上一次出现的位置-1,在当前位置+1;每棵线段树维护的都是1到当前
位置的不同元素的个数。
实现代码:
#include<bits/stdc++.h>
using namespace std; const int M = 3e4+;
const int Max = 1e6+;
int rs[M*],ls[M*],sum[M*],root[M*];
int a[M],idx;
int vis[Max],ret; void update(int old,int &k,int l,int r,int p,int c){
k = ++idx;
ls[k] = ls[old]; rs[k] = rs[old]; sum[k] = sum[old] + c;
if(l == r) return ;
int mid = (l + r) >> ;
if(p <= mid) update(ls[old],ls[k],l,mid,p,c);
else update(rs[old],rs[k],mid+,r,p,c);
} void query(int k,int l,int r,int p){
if(p == l){
ret += sum[k];
return ;
}
int mid = (l + r) >> ;
if(p <= mid) ret += sum[rs[k]],query(ls[k],l,mid,p);
else query(rs[k],mid+,r,p);
return ;
} int main()
{
int n,q;
scanf("%d",&n);
idx = ;
for(int i = ;i <= n;i ++) scanf("%d", &a[i]);
memset(vis,,sizeof(vis));
for(int i = ;i <= n;i ++){
int tmp = ;
if(!vis[a[i]]) update(root[i-],root[i],,n,i,);
else {
update(root[i-],tmp,,n,vis[a[i]],-);
update(tmp,root[i],,n,i,);
}
vis[a[i]] = i;
}
scanf("%d",&q);
while(q--){
int l,r;
scanf("%d %d",&l,&r);
ret = ;
query(root[r],,n,l);
printf("%d\n",ret);
}
return ;
}
ps:
主席树耗时为:200ms;
离线+树状数组
先离线下,对询问的r排序,以元素的下标作树状数组维护以r为右边界的区间不同元素的数量,遍历时如果当前元素没有出现,那么存在他的地址,并在树状数组对应下标+1,如果这个元素
之前已经出现过了,那么取消之前标记的点也就是将这个元素上一次出现的下标在树状数组中-1,变成0,然后再储存下当前元素最迟出现的下标,也就是当前点。
最后区间[l,r]之间不同的数量也就是sumR - sumL;
实现代码:
#include<bits/stdc++.h>
using namespace std; const int Max = 1e6+;
int c[Max],a[Max],n,m,ans[Max],mp[Max];
int lowbit(int x){
return x&(-x);
} int getsum(int x){
int sum = ;
while(x>){
sum += c[x];
x -= lowbit(x);
}
return sum;
} void add(int x,int value){
while(x<=n){
c[x] += value;
x += lowbit(x);
}
} struct node{
int l,r,id;
bool operator < (const node &cmp) const {
return r < cmp.r;
}
}q[Max]; int main()
{
scanf("%d",&n);
memset(mp,-,sizeof(mp));
for(int i = ; i <= n;i ++) scanf("%d",&a[i]);
scanf("%d",&m);
for(int i = ;i <= m;i ++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id = i;
}
sort(q+,q+m+);
int l = ;
for(int i = ;i <= m;i ++){
for(int j = l;j <= q[i].r;j ++){
if(mp[a[j]]!=-){
add(mp[a[j]],-);
}
add(j,);
mp[a[j]] = j;
}
l = q[i].r + ;
ans[q[i].id] = getsum(q[i].r) - getsum(q[i].l-);
} for(int i = ;i <= m;i ++){
printf("%d\n",ans[i]);
}
return ;
}
ps:
离线+树状数组耗时为:150ms
很明显这道题使用离线+树状数组的耗时比主席树和莫队要少很多。。且代码量也比较少;
SPOJ DQUERY - D-query (莫队算法|主席树|离线树状数组)的更多相关文章
- D-query SPOJ - DQUERY(模板莫队)
题意: 给定一个序列,询问m次,每次求出区间 [ L,R ] 有多少个不同数字. 套模板就好了...但我不大明白....我的写法为什么不行...唉... #include <iostream&g ...
- 【魔改】莫队算法+组合数公式 杭电多校赛4 Problem B. Harvest of Apples
http://acm.hdu.edu.cn/showproblem.php?pid=6333 莫队算法是一个离线区间分块瞎搞算法,只要满足:1.离线 2.可以O(1)从区间(L,R)更新到(L±1, ...
- BZOJ 1878 [SDOI2009]HH的项链 (主席树 或 莫队算法)
题目链接 HH的项链 这道题可以直接上主席树的模板 #include <bits/stdc++.h> using namespace std; #define rep(i, a, b) ...
- 莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】
很久以前傻乎乎地看来源奇怪的资料的时候被各种曼哈顿弄晕了. 然后现在学会的是分块方法.另新创一个分块方法. 让我们考虑这样一个区间询问问题…… 它有如下的性质: 0,n个数,Q个询问. 1,它没有修改 ...
- 「日常训练&知识学习」莫队算法(二):树上莫队(Count on a tree II,SPOJ COT2)
题意与分析 题意是这样的,给定一颗节点有权值的树,然后给若干个询问,每次询问让你找出一条链上有多少个不同权值. 写这题之前要参看我的三个blog:Codeforces Round #326 Div. ...
- SPOJ COT2 Count on a tree II 树上莫队算法
题意: 给出一棵\(n(n \leq 4 \times 10^4)\)个节点的树,每个节点上有个权值,和\(m(m \leq 10^5)\)个询问. 每次询问路径\(u \to v\)上有多少个权值不 ...
- HDU 6278 - Just h-index - [莫队算法+树状数组+二分][2018JSCPC江苏省赛C题]
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6278 Time Limit: 6000/3000 MS (Java/Others) Memory Li ...
- C++ 莫队算法(转)
胡小兔的良心莫队教程:莫队.带修改莫队.树上莫队 在开始学习莫队之前,照例先甩一道例题:BZOJ 1878 HH的项链. 题意:求区间内数的个数,相同的数只算一次. 在我关于这道题的上一篇题解中, ...
- Codeforces617 E . XOR and Favorite Number(莫队算法)
XOR and Favorite Number time limit per test: 4 seconds memory limit per test: 256 megabytes input: s ...
随机推荐
- 如何高效的通过BP算法来训练CNN
< Neural Networks Tricks of the Trade.2nd>这本书是收录了1998-2012年在NN上面的一些技巧.原理.算法性文章,对于初学者或者是正在学习NN的 ...
- Exp6
实验内容 一.公开渠道信息搜集 本次信息搜集在metasploit平台上实现(使用msfconsole命令进入) 1.通过DNS和IP挖掘信息 (1)使用whois进行域名信息查询 使用原因:目前互联 ...
- Git配置用户名与邮箱
1.用户名和邮箱地址的作用 用户名和邮箱地址是本地git客户端的一个变量 每次commit都会用用户名和邮箱纪录. github的contributions统计就是按邮箱来统计的. 2.查看用户名和邮 ...
- UWP简单示例(一):快速合成音乐MV
说明 本文发布时间较早,内容可能已过时.最新动态请关注 TypeScript 版本.(2019 年 3 月 注) 在线演示: 音频可视化(TypeScript) 准备 IDE:Visual Studi ...
- rabbitMQ教程(三)一篇文章看懂rabbitMQ
一.rabbitMQ是什么: RabbitMQ,遵循AMQP协议,由内在高并发的erlanng语言开发,用在实时的对可靠性要求比较高的消息传递上. 学过websocket的来理解rabbitMQ应该是 ...
- Express入门介绍vs实例讲解
下午在团队内部分享了express相关介绍,以及基于express的实例.内容提纲如下. 什么是Express 为什么要用Express 路由规则 一切皆中间件 实例:Combo Applicatio ...
- (转载)利用SIFT和RANSAC算法(openCV框架)实现物体的检测与定位,并求出变换矩阵(findFundamentalMat和findHomography的比较) 置顶
原文链接:https://blog.csdn.net/qq_25352981/article/details/46914837#commentsedit 本文目标是通过使用SIFT和RANSAC算法, ...
- laravel从5.2到5.5从入门到精通视频教程共16套
laravel从5.2到5.5从入门到精通视频教程共16套,大部分都是实战项目比如P2P.博客.短网址.知乎门户.app软件开发.微信商城实战等 课程目录: 01.Laravel框架从入门到精通02. ...
- 金蝶盘点机PDA仓库条码管理家电类序列号扫描操作方法-采购入库单
1.1. 采购入库单 传统的进销存管理软件需要人工识别商品品种,清点商品数量,然后再去人工手工在电脑上一行行的录入采购入库单.录单效率低,误差大. 如果使用汉码盘点机PDA,入库时,仓管员只需要手持 ...
- LevelDB原理解析
LevelDb有如下一些特点: 首先,LevelDb是一个持久化存储的KV系统,和Redis这种内存型的KV系统不同,LevelDb不会像Redis一样狂吃内存,而是将大部分数据存储到磁盘上. 其次, ...