P3901 【数列找不同】

这个题我们可以使用树状数组做

啥? 树状数组? 那个不是维护前缀和的东西吗?

各位看官,让我慢慢道来。


首先我们可以想到,对于一个询问$ [l,r] \(,只有\)[1,r]$中的数可能对这个询问有影响。

这就启示我们可以按照询问的右端点进行一波升序排序。

不过这和树状数组有什么关系呢?

对于同一个数\(x\),假设他在\([1,r]\) 出现了若干次。

对于我们的询问\([l,r]\)来说,只有最靠近\(r\)\(x\)才最有可能影响到右端点为\(r\)的询问。

所以,我们就只用记录最靠近\(r\)\(x\)的位置就行了。


那么我们怎么查询一个区间中不同的个数呢?

上文说道,我们只用记录最靠近当前右端点的每个数的位置了。

我们用一个树状数组维护一下。

树状数组中每一个位置就是输入中每一个数的位置。

对于一个数\(x\),他靠近我们当前遍历的右端点的最近一个位置,所对应的树状数组中的数值为1。不是最近的统统为0。

这样的话。对于一个询问\([l,r]\),我们计算一下树状数组中\([l,r]\)中的和。如果和是 \((r-l+1)\)的话,就说\([l,r]\)中所有数都是最靠近\(r\)的,如果不是的话.则说明有重复出现的。


真·凭直觉做题(@某月月赛T1)滑稽。我才不会告诉你我是太蒟蒻了导致什么都不会然后来刷黄题的吗?


类似的题目[SDOI2009]HH的项链](https://www.luogu.org/problemnew/show/P1972)

这个题被某姓毒名瘤字cz的人加强过(暗地%一%),普通莫队会被卡

//Lance1ot
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct node//树状数组
{
    int data[101000];
    int num;
    void updata(int pos,int value)
    {
        while(pos<=num&&pos)
        {
            data[pos]+=value;
            pos+=(pos&(-pos));
        }
        return ;
    }
    int sum(int pos)
    {
        int res=0;
        while(pos)
        {
            res+=data[pos];
            pos-=(pos&(-pos));
        }
        return res;
    }
    int check(int l,int r)
    {
        return sum(r)-sum(l-1);
    }
};
node bit;
struct Query
{
    int l;
    int r;
    int num;
}q[101000];//询问
bool compare(const Query &a,const Query &b)
{
    return a.r<b.r;
}
int data[101000];//原数组
int ans[101000];
int last[101000];//最近一次出现的位置
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    bit.num=n;
    for(int i=1;i<=n;i++)
        scanf("%d",&data[i]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].num=i;//离线处理,是第几个询问。
    }
    sort(q+1,q+1+m,compare);//排序
    int Q=1;
    for(int i=1;i<=n;i++)
    {
        bit.updata(last[data[i]],-1);//撤销上一个记录
        bit.updata(i,1);//更新
        last[data[i]]=i;//保存
        while(q[Q].r==i&&Q<=m)//判断当前右端点是否和某个重合
        {
            if(bit.check(q[Q].l,q[Q].r)==(q[Q].r-q[Q].l+1))//因为是离线算法(fa♂),所以要保存答案
                ans[q[Q].num]=1;
            else
                ans[q[Q].num]=0;
            Q+=1;//写成while保险
        }
    }
    for(int i=1;i<=m;i++)
    {
        if(ans[i]==1)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

速度还算可以吧。

猜你喜欢

转载自www.cnblogs.com/Lance1ot/p/9062420.html