HH的项链 、采花 (莫队,莫队思想结合树状数组)

这篇博客介绍了如何使用莫队算法和树状数组解决区间查询问题,如HH的项链和采花题目。文章提供了两种方法的详细代码实现,并解释了它们的时间复杂度和解决问题的思路。莫队算法适用于处理大量查询,而树状数组则通过前缀和优化达到高效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


HH的项链

这道题主要有两个数据版本
也主要有两个做法
一个是莫队 时 间 复 杂 度 O ( n n ) 时间复杂度O(n \sqrt n) O(nn )
另一个是树状数组
时 间 复 杂 度 O ( n l o g n ) 时间复杂度O(n log n) O(nlogn)


洛谷题目链接
牛客题目链接
牛客的这道题数据会水一点,莫队可以过

题目描述:
在这里插入图片描述
输入样例
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例
2
2
4


第一种主要做法

莫队做法

就是个裸的莫队了,没什么好讲的,直接贴代码了
精彩的主要是第二种做法

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int maxn=1e6+7;
int ans[maxn];
int n;
int block_length;
struct query_node{
	int l,r;
	int id;
}q[maxn];
int a[maxn],cnt[maxn];
inline int read(){
    int n=0;
	char c=getchar();
	bool f=0;
    while(c!='-'&&(c<'0'||c>'9'))c=getchar();
    if(c=='-'){f=1;c=getchar();}
    while(c>='0'&&c<='9'){n=n*10+c-'0';c=getchar();}
    if(f)return -n;
    return n;
}
char write_[50];
inline void write(int n){
    if(n==0){putchar('0');return;}
    if(n<0){putchar('-');n=-n;}
    int t=0;
    while(n){write_[t++]=n%10+'0';n/=10;}
    while(t--)putchar(write_[t]);
}
inline void add(int x){
	if(cnt[a[x]]==0)++ans[0];
	++cnt[a[x]];
}
inline void del(int x){
	--cnt[a[x]];
	if(cnt[a[x]]==0)--ans[0];
}
inline bool cmp(query_node a,query_node b){
    return (a.l/block_length)^(b.l/block_length)?a.l<b.l:(((a.l/block_length)&1)?a.r<b.r:a.r>b.r);
}
int main(){
	n=read();
	for(int i=1;i<=n;++i)a[i]=read();
	int T;
	T=read();
	for(int i=1;i<=T;++i){
		q[i].l=read();q[i].r=read();
		q[i].id=i;
	}
	block_length=sqrt(T);
	sort(q+1,q+T+1,cmp);
	int L=0,R=0;
	for(int i=1;i<=T;++i){
		while(L<q[i].l)del(L++);
		while(R>q[i].r)del(R--);
		while(L>q[i].l)add(--L);
		while(R<q[i].r)add(++R);
		ans[q[i].id]=ans[0];
	}
	for(int i=1;i<=T;++i)write(ans[i]),printf("\n");;
	return 0;
}

第二种做法 —— 树状数组

树状数组做法

这题还可以用树状数组写,感觉有点莫队的思想。
首先我们可以知道对于区间内的重复的数,我们可以只计算最右边的数。
像这样
在这里插入图片描述

那么对于固定的右区间,我们可以通过前缀和来处理出不同左区间带来的答案

在这里插入图片描述
那么我们就可以通过枚举右端点,然后用树状数组O(log)前缀和求出所需要求的区间答案

那么接下来就只剩下处理出各个点权值了
我们记录每个数上一次出现的地方
每次添加一个数的时候,当前位置++,上一次出现的地方–

问题就解决了
很像莫队的思想

AC代码:

#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long

using namespace std;

const double eps = 1e-8;
const int maxn = 2e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1<<30;
inline void swap(int &x, int &y){x^=y^=x^=y;}
inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}

int n, m, k;
int sum[maxn], ans[maxn];
int a[maxn], c[maxn], pre[maxn];
struct q_node{
    int l, r;
    int id;
}q[maxn];
bool cmp(q_node a, q_node b){
    return a.r < b.r;
}
int lowbit(int x){return x & (-x);}
void _add(int x, int w){
    while(x <= n){
        c[x] += w;
        x += lowbit(x);
    }
}
int _sum(int x){
    int sum = 0;
    while(x > 0){
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i)
        scanf("%d %d", &q[i].l, &q[i].r), q[i].id = i;
    sort(q + 1, q + m + 1, cmp);//给区间排序,保证所求区间右端点递增
    int r = 1;//固定左区间
    for(int i = 1; i <= m; ++i){
        while(r <= q[i].r){//处理到右区间
            _add(r, 1);
            if(pre[a[r]])_add(pre[a[r]], -1);
            pre[a[r]] = r;
            r++;
        }
        ans[q[i].id] = _sum(q[i].r) - _sum(q[i].l - 1);//计算答案
    }
    for(int i = 1; i <= m; ++i){
        printf("%d\n", ans[i]);
    }
    return 0;
}

采花

洛谷题目链接
牛客题目链接
思路与上题相同,处理上稍有变化而已


在这里插入图片描述
输入样例
5 3 5
1 2 2 3 1
1 5
1 2
2 2
2 3
3 5
输出样例
2
0
0
1
0

AC代码:

#include <bits/stdc++.h>
#define pii pair<int,int>
#define ll long long

using namespace std;

const double eps = 1e-8;
const int maxn = 2e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1<<30;
inline void swap(int &x, int &y){x^=y^=x^=y;}
inline int gcd(int a,int b) {return !b ? a : gcd(b,a%b);}

int n, m, k;
int ans[maxn];
int a[maxn], c[maxn], pre[maxn], ppre[maxn];
struct q_node{
    int l, r;
    int id;
}q[maxn];
bool cmp(q_node a, q_node b){
    return a.r < b.r;
}
int lowbit(int x){return x & (-x);}
void _add(int x, int w){
    while(x <= n){
        c[x] += w;
        x += lowbit(x);
    }
}
int _sum(int x){
    int sum = 0;
    while(x > 0){
        sum += c[x];
        x -= lowbit(x);
    }
    return sum;
}

int main(){	
    scanf("%d %d %d", &n, &k, &m);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; ++i){

    }
    for(int i = 1; i <= m; ++i)
        scanf("%d %d", &q[i].l, &q[i].r), q[i].id = i;
    sort(q + 1, q + m + 1, cmp);
    int r = 1;
    for(int i = 1; i <= m; ++i){
        while(r <= q[i].r){
            if(pre[a[r]]){
                _add(pre[a[r]], 1);
                if(ppre[a[r]]) _add(ppre[a[r]], -1);
                ppre[a[r]] = pre[a[r]];
            }
            pre[a[r]] = r;
            r++;
        }
        ans[q[i].id] = _sum(q[i].r) - _sum(q[i].l - 1);
    }
    for(int i = 1; i <= m; ++i){
        printf("%d\n", ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值