• 洛谷 P1972 BZOJ 1878 [SDOI2009]HH的项链


    题目描述

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

    输入输出格式

    输入格式:

    第一行:一个整数N,表示项链的长度。

    第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

    第三行:一个整数M,表示HH 询问的个数。

    接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

    输出格式:

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

    输入输出样例

    输入样例#1:
    6
    1 2 3 4 3 5
    3
    1 2
    3 5
    2 6
    
    输出样例#1:
    2
    2
    4

    说明

    数据范围:

    对于100%的数据,N <= 50000,M <= 200000。

    解题思路

      在线解法听说可以用主席树?那玩意太高端,本蒟蒻不会……2019年7月30日21:10:59更新,现在会了HDU 5919 Sequence ll

      离线解法主要有两种——一种是求区间和的巧,另一种是分块莫队。我用的是前一种。

       单点修改求区间和的方法挺多,这里用的树状数组(方便、简单)

      离线,先把所有询问存下来,然后对所有询问按照左端点升序排序(右端点排不排无所谓的啦,反正又不影响结果)。

      然后记录下每种颜色的下一个相同颜色所在的位置,用next[i]表示。

      然后在树状数组(开始时全部是0)中把每种颜色第一次出现的位置标记为1,然后就可以按排好的顺序处理所有询问了。

      一个个询问可以看作是一个滑动窗口在项链上滑动,读取窗口内的颜色信息。

      窗口向右移动以后,左端点的左边那些第一次出现的颜色在窗口中就表示不出来了,这时就要把左端点以左的标记为1颜色全部“弄”到下一个颜色相同的点上(next[i])。

      定义一个变量$l$,然后按之前的左端点递增的顺序处理询问。

      让$l$从1开始不断增加,直到到达第$i$个询问所要求的左端点前一位,这部分的作用是把当前询问区间的左边那些1全部“弄”到后面。然后统计这次询问的答案,线段树求这个询问区间的区间和就是答案啦!

    ----------------------------------------------------分割线-------------------------------------------------------

      2019年03月02日16:57:57。我当年写的啥玩意……丢个我现在看得懂的链接。为啥我的代码比黄学长长那么多……2019年03月02日17:09:23把上面的描述更新了一波,好像好懂多了(说不定一年多以后我又看不懂了)。

    ----------------------------------------------------分割线-------------------------------------------------------

      我来填坑啦!并不会输公式,勉强看看吧

      莫队离线处理询问区间(li,ri),如果知道了(li,ri)的答案,那我们就能很快知道(li-1,ri)(li+1,ri)(li,ri-1)(li,ri+1)的答案,那么在已知(l[i],r[i])的答案的情况下,我们需要O(abs(l[i]-l[i+1])+abs(r[i]-r[i+1]))的时间暴力得到(l[i+1],r[i+1])的答案。如何使转移代价最少?一种方法是按曼哈顿最小生成树的dfs序转移,但是难度过高,于是——

      对于每个读入的询问(l[i],r[i]),我们设pos[i]=l[i]/sqrt(n),(这就是传说中的分块)以pos[i]为第一关键字,r[i]为第二关键字对所有询问进行排序,依次处理询问,这样能跑得很快。时间复杂度变成了O(n^(3/2))。

      证明和代码网上遍地都是,这里就不贴了(逃)。下面的代码思路是树状数组的。

    源代码

    #include<cstdio>
    #include<algorithm>
    #define lowbit(x) (x)&(-x)
    
    int n,m,mx=-1;
    int a[50010]={0},next[50010]={0};
    int p[1000010]={0};//类似一个桶,装每种颜色第一次出现的位置
    
    
    int c[50010]={0};//树状数组
    int query(int pos)
    {
        int ans=0;
        while(pos)
        {
            ans+=c[pos];
            pos-=lowbit(pos);
        }
        return ans;
    }
    void add(int pos,int x)
    {
        while(pos<=n)
        {
            c[pos]+=x;
            pos+=lowbit(pos);
        }
    }
    
    struct Ask{
        int l,r,id,ans;
    }ask[200010];
    bool cmp1(const Ask & a,const Ask & b)
    {
        return a.l==b.l?a.r<b.r:a.l<b.l;
    }
    bool cmp2(const Ask & a,const Ask & b)
    {
        return a.id<b.id;
    }
    
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",a+i),mx=std::max(a[i],mx);
        for(int i=n;i>0;i--)//预处理出next和p
        {
            next[i]=p[a[i]];
            p[a[i]]=i;
        }
        for(int i=1;i<=mx;i++) if(p[i])add(p[i],1);
        
        scanf("%d",&m);
        for(int i=1,l,r;i<=m;i++)
        {
            scanf("%d%d",&l,&r);
            ask[i]={l,r,i,0};
        }
        
        std::stable_sort(ask+1,ask+1+m,cmp1);
      //用归并的原因是,apio2017,第二题交互题,某部分分可以用stl排序的比较函数发出询问,然后就能得到结果。 这个页面三分之二的位置
      //询问次数有限,用归并才能以较少的比较函数调用次数卡过去,好些大佬就这样被卡了85。当时半懂不懂的,apio以后用了好久归并,不敢用快排#捂脸
    for(int i=1,ll=0;i<=m;i++) { while(ll<ask[i].l) { if(next[ll]) add(next[ll],1); ll++; } ask[i].ans=query(ask[i].r)-query(ask[i].l-1); } std::stable_sort(ask+1,ask+1+m,cmp2); for(int i=1;i<=m;i++) printf("%d ",ask[i].ans); return 0; }
  • 相关阅读:
    BZOJ3209: 花神的数论题
    BZOJ3207: 花神的嘲讽计划Ⅰ
    BZOJ3155: Preprefix sum
    BZOJ2465: [中山市选2009]小球
    BZOJ2243: [SDOI2011]染色
    BZOJ1192: [HNOI2006]鬼谷子的钱袋
    hdu1542(线段树——矩形面积并)
    hdu4578(线段树)
    hdu4614(线段树+二分)
    hdu3974(线段树+dfs)
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/6901275.html
Copyright © 2020-2023  润新知