[bzoj2743][HEOI2012]采花

本文介绍了一种针对大量区间查询问题的有效解决方法,通过预处理和树状数组维护重复元素,实现对每个询问区间的快速响应。适用于序列长度及询问次数较大的场景。

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

Description

给出一个长度为n的序列,序列中的数再[1~c]的范围内,和m次询问,每次询问[l,r]这个区间中有多少个出现大于等于两次的数。
n,m,c<=10^6

Solution

看到这种题,有没有强制在线,自然很容易想到莫队~(≧▽≦)/~啦啦啦
可是n<=10^6,根号n的莫队表示很方。( ⊙ o ⊙ )啊!
于是,我们可以想到一个优美的替代品。
预处理出next[i]表示i后面第一个和i颜色相同的位置。
然后,把所有第二次出现的位置+1.
用树状数组维护。
把所有询问按左端点排序,处理完所有以i开始的讯问后,我们把next[i]的影响删除,把next[next[i]]加上。这样就能保证计算入答案的必然是这个区间里第二次出现的。
然后就没有了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 1000005
using namespace std;
struct note{int l,r,id;}ask[N];
bool cmp(note x,note y){
    return x.l<y.l;
}
int n,m,c,ans,a[N],next[N],f[N],t[N],an[N];
void add(int x,int y) {
    for(;x<=n;x+=x&(-x)) t[x]+=y;
}
int find(int x) {
    for(ans=0;x;x-=x&(-x)) ans+=t[x];
    return ans;
}
int main() {
    scanf("%d%d%d",&n,&c,&m);
    fo(i,1,n) {
        scanf("%d",&a[i]);
        if (f[a[i]]) next[f[a[i]]]=i;
        f[a[i]]=i;
    }memset(f,0,sizeof(f));
    fo(i,1,n) if (next[i]&&!f[a[i]]) f[a[i]]=1,add(next[i],1);
    fo(i,1,m) scanf("%d%d",&ask[i].l,&ask[i].r),ask[i].id=i;
    sort(ask+1,ask+m+1,cmp);int j=0;
    fo(i,1,n) {
        while (ask[j+1].l==i) an[ask[++j].id]=find(ask[j].r);
        if (next[i]) add(next[i],-1);
        if (next[next[i]]) add(next[next[i]],1);
    }
    fo(i,1,m) printf("%d\n",an[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值