zoukankan      html  css  js  c++  java
  • 题解:bzoj1878: [SDOI2009]HH的项链

    Description

    HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一
    段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一
    个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只
    好求助睿智的你,来解决这个问题。

    Input

    第一行:一个整数N,表示项链的长度。 
    第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 
    第三行:一个整数M,表示HH询问的个数。 
    接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
    N ≤ 50000,M ≤ 200000。

    Output

    M行,每行一个整数,依次表示询问对应的答案。

    Sample Input

    6
    1 2 3 4 3 5
    3
    1 2
    3 5
    2 6

    Sample Output

    2
    2
    4
     
    题解:
     
    离线询问+树状数组
    实际上是用树状数组维护区间不同数。可以考虑记录前i个数中不同数的个数,对于询问q(l,r),就像前缀和那样,用num[r]-num[l-1]计算答案。
    事实上这样的算法并不正确,比如1 2 3 3 5,num[4]=3,num[5]=4,num[5]-num[4-1]=1,但q(4,5)答案应该是2。
    不难发现,这种算法的bug在于num[i]记录的是[1,i],由于3已经在前面出现过。故对于询问q(l,r)时,若[l,r]存在[1,l]出现过的数时,就会少算。所以可以引入一个next数组,next[i]记录当前位置i表示的数下一次出现的位置,当处理到第i位,便修改其next[i]指向的位置到全的前缀和,这样,就可以用num[r]-num[i-1]计算了
    对询问离线并按左边界从小到大排序,用pos[i]表示数i第一次出现的位置,然后按顺序处理询问:对整个区间从左到右扫,每扫到一个数用树状数组修改其next的前缀和,直到碰到一个询问的左边界,然后按num[r]-num[i-1]计算答案。
    可以我的结合代码理解。
    #include<bits/stdc++.h>
    using namespace std;
    struct question{
        int l,r,ans,idx;
    }q[200005];
    int n,m,mx=0;
    int necklace[50005],pos[1000005],nextx[50005],node[50005];
    int lowbit(int a)
    {
        return a&(-a);
    }
    int add(int k,int val)
    {
        for(;k<=n;k+=lowbit(k))
            node[k]+=val;
    }
    int ask(int k)
    {
        int sum=0;
        for(;k>0;k-=lowbit(k))
            sum+=node[k];
        return sum;    
    }
    int maxn(int a,int b)
    {
        return a>b?a:b;
    }
    bool cmp1(question a,question b)
    {
        return a.l<b.l;
    }
    bool cmp2(question a,question b)
    {
        return a.idx<b.idx;
    }
    int main()
    {
        memset(pos,0,sizeof(pos));
        memset(nextx,0,sizeof(nextx));
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            {
                scanf("%d",&necklace[i]);
                mx=maxn(mx,necklace[i]);
            }
        for(int i=n;i>=1;i--)
            {
                nextx[i]=pos[necklace[i]];
                pos[necklace[i]]=i;
            }
        for(int i=1;i<=mx;i++)
            if(pos[i])
               add(pos[i],1);
        scanf("%d",&m);       
        for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&q[i].l,&q[i].r);
                q[i].idx=i;
            }
        sort(q+1,q+1+m,cmp1);
        int L=1;
        for(int i=1;i<=m;i++)
            {
                while(L<q[i].l)
                      {
                           if(nextx[L])
                              add(nextx[L],1);
                           L++;         
                      }
                q[i].ans=ask(q[i].r)-ask(q[i].l-1);      
            }
        sort(q+1,q+1+m,cmp2);
        for(int i=1;i<=m;i++)
            printf("%d\n",q[i].ans);         
    } 
    View Code
    小鳥の翼がついに大きくなって , 旅立ちの日だよ , 遠くへと広がる海の色暖かく , 夢の中で描いた絵のようなんだ , 切なくて時をまきもどしてみるかい ? No no no いまが最高! だってだって、いまが最高!
  • 相关阅读:
    PHP数组(数组正则表达式、数组、预定义数组)
    面向对象。OOP三大特征:封装,继承,多态。 这个讲的是【封存】
    uvalive 3938 "Ray, Pass me the dishes!" 线段树 区间合并
    LA4329 Ping pong 树状数组
    HDU 1257 最少拦截系统
    HDU 1260 Tickets
    codeforce 621D
    codeforce 621C Wet Shark and Flowers
    codeforce 621B Wet Shark and Bishops
    codeforce 621A Wet Shark and Odd and Even
  • 原文地址:https://www.cnblogs.com/nanjolno/p/8974131.html
Copyright © 2011-2022 走看看