BZOJ1878 洛谷1972 HH的项链题解

洛谷链接

BZOJ链接

看到这样不用修改的题目,应该佷容易就联想到了离线来处理。

我们发现若将询问按照r来排序,排完后每次对答案有贡献的仅是每个颜色最后出现的位置

我们用next[i]表示i处颜色之前出现的位置,我们利用树状数组维护每个数最后一次出现的位置小于j的个数,每次的答案就是树状数组l到r这一段的和

显然,next[i]处对答案已经不会产生贡献了,我们就可以将这个贡献减掉

然后再将i处对答案的贡献加入

# include<iostream>
# include<cstdio>
# include<cmath>
# include<algorithm>
using std::sort;
const int mn = 500005;
const int maxn = 200005;
inline int read()
{
    int x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') ch=getchar();
    while(ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,m;
int nxt[1000005],ans[maxn],tmp[1000005];
int T[mn];
//nxt[i]表示在i处的颜色上一次出现的位置
void add(int x,int val)
{
    while(x<=n)
    {
        T[x]+=val;
        x+=(x&-x);
    }
}
int sum(int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=T[x];
        x-=(x&-x);
    }
    return ret;
}
struct ask{int l,r,id;};
ask b[maxn];
bool cmp(ask x,ask y)
{
    return x.r<y.r;
}
int main()
{
    int x;
    n=read();
    for(int i=1;i<=n;i++)
    {
        x=read(),nxt[i]=tmp[x],tmp[x]=i;
    }
    m=read();
    for(int i=1;i<=m;i++)
      b[i].l=read(),b[i].r=read(),b[i].id=i;
    sort(b+1,b+1+m,cmp);
    int l=1;
    for(int i=1;i<=m;i++)
    {
        while(l<=b[i].r)
        {
            //若上一次出现过,要把上一次统计的结果减掉
            if(nxt[l])
                add(nxt[l],-1);
            add(l,1);
            l++;
        }
        ans[b[i].id]=sum(b[i].r)-sum(b[i].l-1);
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

posted @ 2018-06-14 13:29  logeadd  阅读(164)  评论(0编辑  收藏  举报