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

    1878: [SDOI2009]HH的项链

    Time Limit: 4 Sec  Memory Limit: 64 MB
    Submit: 1673  Solved: 808
    [Submit][Status]

    Description

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

    Input

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

    Output

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

    Sample Input

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

    Sample Output

    2
    2
    4

    HINT


    对于20%的数据,N ≤ 100,M ≤ 1000;
    对于40%的数据,N ≤ 3000,M ≤ 200000;
    对于100%的数据,N ≤ 50000,M ≤ 200000。

    Source

    题解:
    这题以前做树状数组的时候做过,只不过现在早把思想忘了,今天再来做一下。
    巧妙的运用了前缀和,使得树状数组发挥作用。
    这题树状数组有两种写法,具体见代码:
    1.按右端点排序
     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<iostream>
     7 #include<vector>
     8 #include<map>
     9 #include<set>
    10 #include<queue>
    11 #define inf 1000000000
    12 #define maxn 50000+1000
    13 #define maxm 200000+10000
    14 #define maxk 1000000+1000
    15 #define eps 1e-10
    16 #define ll long long
    17 #define pa pair<int,int>
    18 using namespace std;
    19 inline int read()
    20 {
    21     int x=0,f=1;char ch=getchar();
    22     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    23     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
    24     return x*f;
    25 }
    26 struct rec{int x,y,id;}a[maxm];
    27 int s[maxn],ans[maxm],next[maxn],head[maxk],n,m;
    28 void add(int x,int y)
    29 {
    30     for(;x<=n;x+=x&(-x))s[x]+=y;
    31 }
    32 int sum(int x)
    33 {
    34     int t=0;
    35     for(;x;x-=x&(-x))t+=s[x];
    36     return t;
    37 }
    38 bool cmp(rec a,rec b)
    39 {
    40     return a.y<b.y;
    41 }
    42 int main()
    43 {
    44     freopen("input.txt","r",stdin);
    45     freopen("output.txt","w",stdout);
    46     n=read();
    47     for(int i=1;i<=n;i++)
    48      {
    49          int x=read();
    50          next[i]=head[x];
    51          head[x]=i;
    52      }
    53     m=read();
    54     for(int i=1;i<=m;i++)
    55      a[i].x=read(),a[i].y=read(),a[i].id=i;
    56     sort(a+1,a+m+1,cmp);
    57     int now=0;
    58     for(int i=1;i<=m;i++)
    59     {
    60         while(now<a[i].y)
    61         {
    62             now++;
    63             add(next[now]+1,1);
    64             add(now+1,-1);
    65         }
    66         ans[a[i].id]=sum(a[i].x);
    67     }  
    68     for(int i=1;i<=m;i++)printf("%d
    ",ans[i]);
    69     return 0;
    70 }
    View Code

    2.按左端点排序

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cmath>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<iostream>
     7 #include<vector>
     8 #include<map>
     9 #include<set>
    10 #include<queue>
    11 #define inf 1000000000
    12 #define maxn 50000+1000
    13 #define maxm 200000+10000
    14 #define maxk 1000000+1000
    15 #define eps 1e-10
    16 #define ll long long
    17 #define pa pair<int,int>
    18 using namespace std;
    19 inline int read()
    20 {
    21     int x=0,f=1;char ch=getchar();
    22     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    23     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
    24     return x*f;
    25 }
    26 struct rec{int x,y,id;}a[maxm];
    27 int s[maxn],ans[maxm],next[maxn],head[maxk],n,m,mx,b[maxn];
    28 void add(int x,int y)
    29 {
    30     for(;x<=n;x+=x&(-x))s[x]+=y;
    31 }
    32 int sum(int x)
    33 {
    34     int t=0;
    35     for(;x;x-=x&(-x))t+=s[x];
    36     return t;
    37 }
    38 bool cmp(rec a,rec b)
    39 {
    40     return a.x==b.x?a.y<b.y:a.x<b.x;
    41 }
    42 int main()
    43 {
    44     freopen("input.txt","r",stdin);
    45     freopen("output.txt","w",stdout);
    46     n=read();
    47     for(int i=1;i<=n;i++)b[i]=read();
    48     for(int i=n;i>=1;i--)
    49      {
    50          next[i]=head[b[i]];
    51          head[b[i]]=i;
    52          mx=max(mx,b[i]);
    53      }
    54     m=read();
    55     for(int i=1;i<=m;i++)
    56      a[i].x=read(),a[i].y=read(),a[i].id=i;
    57     sort(a+1,a+m+1,cmp);
    58     for(int i=1;i<=mx;i++)if(head[i])add(head[i],1);
    59     int now=1;
    60     for(int i=1;i<=m;i++)
    61     {
    62         while(now<a[i].x)
    63         {
    64             if(next[now])add(next[now],1); 
    65             now++;
    66         }
    67         ans[a[i].id]=sum(a[i].y)-sum(a[i].x-1);
    68     }  
    69     for(int i=1;i<=m;i++)printf("%d
    ",ans[i]);
    70     return 0;
    71 }
    View Code

    本题数据规模较大,需要合理使用数据结构。题目中有两个元素:区间和颜色。如果直接使用线段树套平衡树,需要牵扯到合并操作,时间复杂度很高(比如当询问类似[2,N-1]的时候)。因此,需要做一些预处理工作使得询问更容易解决。

    观察在某一区间内出现的某种颜色,假设有若干个同色点都在这个区间中,而该颜色只能计算一遍,因此我们需要找出一个有代表性的点,当然是第一个最有代表性。观察该点,以及它前后同色点的位置,有什么可以利用的规律?很显然,它的上一个同色点肯定在区间左侧,而同区间内的该颜色的其他点,它们的上一个同色点显然在区间内。这样,我们的工作便是找到询问区间[l,r]中这样的点x的个数:点x的上一个同色点在询问区间[l,r]左侧。

    到这一步,我们有一个在线算法:建线段树,将询问分成O(lbN)个小区间分别处理。如果再套平衡树,需要二分查找才能求符合条件的点的个数。这样总的理论最坏复杂度为O(M(lbN)3),虽然实际情况会好一些,但还是难以符合要求。我们分析一下该算法复杂度高的原因:平衡树难以很好支持求个数的运算,需要二分来实现。那么求个数效率最高的是什么数据结构?当然是树状数组。

    当然,线段树是无法再套树状数组的,那样空间复杂度会达到O(N2),所以我们要舍弃线段树,寻找新的算法。对于一堆无序的询问区间,如果没有线段树,便很难处理。因此我们考虑将区间按左端点排序,使其有序,然后从左到右扫描每个点。再考虑那些上一个同色点在询问区间左侧的点,我们的目的是快速求出该区间中这种点的个数。而这些点的上一个同色点因为在询问区间左侧,所以已经被扫描过,而区间内其他点却没有!这样,如果我们换个角度,改上一个下一个,预处理出每个点i的下一个同色点的位置Next[i],并且在扫描过该点i后使树状数组C[Next[i]]=1。那么对于询问区间[l,r],答案便是Sumc[r]-Sumc[l-1]!这样,算法只有1”求和两种操作。问题到此得到圆满解决, 最终时间复杂度为O(MlbN)

  • 相关阅读:
    zoj 1239 Hanoi Tower Troubles Again!
    zoj 1221 Risk
    uva 10192 Vacation
    uva 10066 The Twin Towers
    uva 531 Compromise
    uva 103 Stacking Boxes
    稳定婚姻模型
    Ants UVA
    Golden Tiger Claw UVA
    关于upper、lower bound 的探讨
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3935617.html
Copyright © 2011-2022 走看看