牛客多校训练第一场 J.Different Integers

题目

有一个长度为n的数字序列,有m个询问。每一个询问有(l,r),代表的意思是求从1..l和r...n中不同数字的个数。

思路

这个题一开始想用莫队做··但不知道为什么一直T。就还是按题解的思路写把。

标程的前置技能是树状数组和询问离线化。可以用HDU3874/HDU3333去练习一下。

其实我在写完上面说的两道题之后还是不明白应该怎么写,所以看了几个人的代码。然后还是没看明白····只看懂了两个数组标记每一个数字出现的第一个位置和最后一个位置。具体干什么的也不知道,后来把别人的代码自己打了一遍才知道是怎么回事····被自己蠢到了。

然后正式开始:

​ 首先对这个题分析的时候肯定能想到对于询问进行离线,思路和上面我说的那两个题目是一样的,问题出在怎么统计答案上。所以用到了两个数组,分别对于每一个数字标记第一次和最后一次出现的位置,然后在更新R的时候,把扫描到的数字进行判断,如果是最后一次出现,那就把这个数字第一次出现的位置放到树状数组里,同时总数减一。这样在最后统计的时候,把总数加上1到当前询问的左区间的值之和就是应该的答案。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
int a[maxn],c[maxn],ans[maxn],fir[maxn],last[maxn];
int n;
struct Node{
    int l,r,id;
    bool operator <(const Node &ths)const{
        return r<ths.r;
    }
}que[maxn];
inline int lowbit(int x)
{
    return x&(-x);
}
int query(int x)
{
    int now=0;
    while(x)
    {
        now+=c[x];
        x-=lowbit(x);
    }
    return now;
}
void change(int x)
{
    while(x<=n)
    {
        c[x]++;
        x+=lowbit(x);
    }
}
int main()
{
    int m,cnt;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        cnt=0;
        memset(last,0,sizeof(last));
        memset(fir,0,sizeof(fir));
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            if(fir[a[i]]==0)
                fir[a[i]]=i,cnt++;
            last[a[i]]=i;
        }
        for(int i=1;i<=m;++i){
            scanf("%d%d",&que[i].l,&que[i].r);
            que[i].id=i;
        }
        sort(que+1,que+1+m);
        int r=1;
        for(int i=1;i<=m;++i){
            while(r<que[i].r)
            {
                if(last[a[r]]==r){//核心操作
                    change(fir[a[r]]);
                    cnt--;
                }
                ++r;
            }
            ans[que[i].id] = cnt+query(que[i].l);
        }
        for(int i=1;i<=m;++i)
            printf("%d\n",ans[i]);

    }
    return 0;
}
posted @ 2018-07-20 19:49 SCaryon 阅读(...) 评论(...) 编辑 收藏