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 ...
随机推荐
- (转)Centos7 修改硬件时间和系统时间
查看硬件时间 [root@localhost ~]# hwclock --show Tue 13 Jun 2017 02:11:12 AM CST -0.848845 seconds 1 2 可以看出 ...
- abp 嵌入资源(视图、css、js)的访问
最近在做的基于abp作为框架的一个项目,将一些属于框架功能的页面写在了一个独立程序集中,然后在web项目中引用该程序集达到访问框架页面目的. 这样一来发布web之后,在发布目录中是看不到写在另一个程序 ...
- 采用PowerDesigner 设计数据库
PowerDesigner设计数据库的教程网上都有,最好的是我一位同学写的,地址: 点击这里 我的大致流程如下: 首先要以管理员的身份打开PowerDesigner,如果没这么做,将导致后面无法创建S ...
- 写个定时任务更新svn
最近学了点shell编程,寻思锻炼下写一个.平常你学习或者看别人讲,自己不练习肯定不行,基本上一动手准出错哈哈.等自己去实践,才会知道哪里有问题,哪里容易出错,哪里要注意什么的. 因为我们每个人有自己 ...
- sql 两表更新
UPDATE sale_origin_line set state='cancel' from sale_origin p,sale_origin_line q where p.id=q.or ...
- 大数据入门第二十五天——elasticsearch入门
一.概述 推荐路神的ES权威指南翻译:https://es.xiaoleilu.com/010_Intro/00_README.html 官网:https://www.elastic.co/cn/pr ...
- AT24C02跨页写数据
AT24C02 EEPROM的写数据分为:字节写数据模式和页写数据模式:字节写就是一个地址一个数据的写,页写是连续写数据,一个地址多个数据的写,但是页写不能自动跨页,如果超出一页长度,超出的数据会覆盖 ...
- LOJ#6354. 「CodePlus 2018 4 月赛」最短路[最短路优化建图]
题意 一个 \(n\) 个点的完全图,两点之间的边权为 \((i\ xor\ j)*C\) ,同时有 \(m\) 条额外单向路径,问从 \(S\) 到 \(T\) 的最短路. \(n\leq 10^5 ...
- Harbor私有镜像仓库无坑搭建
转载:https://k8s.abcdocker.com/kubernetes_harbor.html 一.介绍 Docker容器应用的开发和运行路不开可靠的镜像管理,虽然Docker官方也提供了公共 ...
- Ubuntu命令行运行C程序和C++程序
首先Ctrl + T 打开一个终端,cd到你建立C/C++文件的目录下. 下面以建立 helloc.c 和 hellocpp.cpp 进行演示 vim helloc.c 按 i 进入插入操作,然后写C ...