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)

  • 相关阅读:
    八进制转换成十进制(你会明白的,呵呵)
    从键盘读取7个数(150)的整数值,每读一个值打印出该值个数的*号.
    两个字符串的连接程序
    判断一个素数能被几个9整除.
    809*??=800*??+9*??+1 其中??代表的两位数,8*??的结果为两位数,9*??的结果为3位数。求??代表的两位数,及809*??后的结果。
    一个偶数总能表示为两个素数的和.
    07所能组成的奇数的个数
    asp.net .ashx文件使用Server.MapPath解决方法
    MVC常见问题小总结
    amcharts_2.6.13左上角的广告咱去掉
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3935617.html
Copyright © 2011-2022 走看看