zoukankan      html  css  js  c++  java
  • [Violet 6]蒲公英

    题目

    Prob

    Description

    亲爱的哥哥:

    你在那个城市里面过得好吗?

    我在家里面最近很开心呢。昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被它杀掉了。我觉得把那么可怕的怪物召唤出来的那个坏蛋也很坏呢。不过奶奶说他是很难受的时候才做出这样的事的……

    最近村子里长出了一大片一大片的蒲公英。一刮风,这些蒲公英就能飘到好远的地方了呢。我觉得要是它们能飘到那个城市里面,让哥哥看看就好了呢!

    哥哥你要快点回来哦!

    爱你的妹妹 Violet

    Azure 读完这封信之后微笑了一下。

    “蒲公英吗……”

    在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

    为了简化起见,我们把所有的蒲公英看成一个长度为 n 的序列 {a1,a2..an},其中 ai 为一个正整数,表示第 i 棵蒲公英的种类编号。

    而每次询问一个区间 [l,r],你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

    注意,你的算法必须是在线的。

    Input

    第一行有两个整数,分别表示蒲公英的数量 n 和询问次数 m。

    第二行有 n 个整数,第 i 个整数表示第 i 棵蒲公英的种类 ai

    接下来 m 行,每行两个整数 l_0, r_0,表示一次询问。输入是加密的,解密方法如下:

    令上次询问的结果为 x(如果这是第一次询问,则 x=0),设 l=((l_0+x1mon)+1,r=((r_0+x1mon)+1。如果 l>r,则交换 l,r。
    最终的询问区间为计算后的 [l,r]。

    Output

    对于每次询问,输出一行一个整数表示答案。

    Sample Input

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

    Sample Output

    1
    2
    1

    Hint

    数据规模与约定

    • 对于 20% 的数据,保证 n,m3000。
    • 对于 100% 的数据,保证 1n40000,1m50000,1ai10^9, 1l_0,r_0n。

    思路

    这是一道经典的区间求众数问题;

    已知 区间$[x,y]$ 的众数和 区间$[y+1,z]$ 不能直接得到 区间$[x,z]$ 的众数;

    所以用树状数组或线段树维护就非常困难;

    所以我们可以使用分块做法;

    首先我们可以预处理出每两个块之间的众数;

    然后我们可以设一个 $vetor[i]$ 来保存 $i$ 这个数出现的位置;

    ve[aa[i]].push_back(i);

    记下数字 $aa[i]$ 出现的位置 $i$;

    对于每个询问,在对应的 $vector$ 里二分查找即可得到 $x$ 在 $[l,r]$ 中出现的次数;

    例如:

    序列 $aa~=~{1,4,3,2,2,3,1,4,1,3,4} ~$ 

    $vector[1]={1,7,9}$  ,  $1$ 出现的位置是 $1,7,9$ ;

    $vector[2]={4,5}$     ,  $2$ 出现的位置是 $4,5$ ;

    $vector[3]={3,6,10}$  ,$1$ 出现的位置是 $3,6,10$ ;

    $vector[4]={2,8,11}$  ,$4$ 出现的位置是 $2,8,11$ ;

    当我们访问区间 $[2,8]$ 的众数时;

    我们可以在$vetor[x]$ 里找第一个 $ geq~l$ 的数的下标,和第一个 $ leq~r$ 的数的下标,然后把两下标相减加一即可;

    inline ll findout(ll x,ll y,ll t)//找区间[x,y]中 ,t 出现的次数
    {
        ll sum=upper_bound(ve[t].begin(),ve[t].end(),y)
              -lower_bound(ve[t].begin(),ve[t].end(),x);
        return sum;
    }

    求$1$ 在 区间$[2,8]$里 出现的次数,$vector[1]$ 中第一个 $ geq~l$ 的数组下标是 $2$,第一个 $ leq~r$ 的数的下标是 $2$,所以 $1$ 在区间 $[2,8]$ 中出现了 $2-2+1=1$ 次;

    求$2$ 在 区间$[2,8]$里 出现的次数,$vector[2]$ 中第一个 $ geq~l$ 的数组下标是 $1$,第一个 $ leq~r$ 的数的下标是 $2$,所以 $2$ 在区间 $[2,8]$ 中出现了 $2-1+1=2$ 次;

    求$3$ 在 区间$[2,8]$里 出现的次数,$vector[3]$ 中第一个 $ geq~l$ 的数组下标是 $1$,第一个 $ leq~r$ 的数的下标是 $2$,所以 $3$ 在区间 $[2,8]$ 中出现了 $2-1+1=2$ 次;

    求$4$ 在 区间$[2,8]$里 出现的次数,$vector[4]$ 中第一个 $ geq~l$ 的数组下标是 $1$,第一个 $ leq~r$ 的数的下标是 $2$,所以 $4$ 在区间 $[2,8]$ 中出现了 $2-1+1=2$ 次;

    所以区间$[2,8]$ 的众数是

    $2$  !!!

    这样就好了,敲得我好累;

    然后就只剩暴力了。。。。。。。。。。。

    等等!!

    还有要注意的是题目中的 $a[i]~leq~10^9$ ,尼玛,太大了;

    那么只能乖乖写离散化了,然后就还要记录每个数以前的标号;

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }//为什么这里一片死寂 
    ll n,m,blo;
    ll a[50005],f[505][505];
    ll num[50005],aa[50005],v[50005];
    map<ll,ll>ff;
    vector<ll> ve[50005];
    inline ll L(ll x)
    {
        return (x-1)*blo+1;
    }// x 分块的左端点 
    inline ll R(ll x)
    {
        return x*blo;
    }// x 分块的右端点 
    inline void beforework(ll x)//beforework 在我们做事之前 
    {
        memset(num,0,sizeof(num));
        ll s=0,mx=0;
        for(ll i=L(x);i<=n;i++) 
        {
            num[aa[i]]++;//暴力记录每个数出现的次数 
            if(num[aa[i]]>mx||num[aa[i]]==mx&&v[aa[i]]<v[s])//如果出现的次数多,或者出现次数一样但数的标号小 
                s=aa[i],mx=num[aa[i]];//保存下新的众数 
            f[x][a[i]]=s;//那么 s 就是 分块 x-a[i] 中新的众数 
        }
    }
    inline ll findout(ll x,ll y,ll t)//找区间[x,y]中 ,t 出现的次数
    {
        //vetor[t] 里找第一个 >=l 的数的下标,和第一个 <=r 的数的下标,然后把两下标相减加一即可;
        ll sum=upper_bound(ve[t].begin(),ve[t].end(),y)
              -lower_bound(ve[t].begin(),ve[t].end(),x);
        return sum;
    }
    inline ll reallyans(ll x,ll y)
    {
        ll ans=f[a[x]+1][a[y]-1];//中间大分块已经处理过了,直接拿来用 
        ll mx=findout(x,y,ans);//但是需要再求一遍 众数 出现的 次数 ,方便统计新的众数 
        if(a[x]==a[y])//如果他们在一个分块里 
        {
            for(ll i=x;i<=y;i++)
            {
                ll tot=findout(x,y,aa[i]);//直接通过 二分暴力找出现次数 
                if(tot>mx||tot==mx&&v[aa[i]]<v[ans])
                    ans=aa[i],mx=tot;
            }
        }
        else//x y 不在同一分块 
        {
            //中间的大分块已经预处理过了,只要暴力小分块就好了 
            for(ll i=x;i<=R(a[x]);i++)
            {
                ll tot=findout(x,y,aa[i]);//直接通过 二分暴力找出现次数 
                if(tot>mx||tot==mx&&v[aa[i]]<v[ans])//比较看是否出现了新的众数 
                    ans=aa[i],mx=tot;
            }
            for(ll i=L(a[y]);i<=y;i++)
            {
                ll tot=findout(x,y,aa[i]);//直接通过 二分暴力找出现次数 
                if(tot>mx||tot==mx&&v[aa[i]]<v[ans])//比较看是否出现了新的众数
                    ans=aa[i],mx=tot;
            }
        }
        return ans;//返回答案 
    }
    int main()
    {
        n=read();m=read();//读入 
        blo=sqrt(n);//分成 blo 个块 
        ll t=0;
        for(ll i=1;i<=n;i++)
        {
            aa[i]=read();
            a[i]=(i-1)/blo+1;//记录每一个数属于那个块 
            if(!ff[aa[i]])
            {
                ff[aa[i]]=++t;//离散化出新的标号 
                v[t]=aa[i];//记录以前的标号 
            }
            aa[i]=ff[aa[i]];//附上新的标号 
            ve[aa[i]].push_back(i);//记下每个数的出现位置 
        }
        for(ll i=1;i<=a[n];i++)
            beforework(i);//预处理出每两个分块的众数 
        ll ans=0;
        while(m--)
        {
            ll x=read(),y=read();
            x=(x+ans-1)%n+1;y=(y+ans-1)%n+1;//解密 
            if(x>y)
                swap(x,y);
            ans=v[reallyans(x,y)];//find 答案 
            printf("%lld
    ",ans);//输出 
        }
        return 0;//终于没忘打 return 0; 
    } 
  • 相关阅读:
    Spring AOP @Before @Around @After 等 advice 的执行顺序
    web服务启动spring自动执行ApplicationListener的用法
    Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法
    ApplicationContextAware使用理解
    查询oracle比较慢的session和sql
    使用ANTS Performance Profiler&ANTS Memory Profiler工具分析IIS进程内存和CPU占用过高问题
    C#控制台程序中处理2个关闭事件的代码实例
    mysql优化----第一篇:综述
    HMM 隐马尔科夫模型
    EM 期望最大化算法
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13442641.html
Copyright © 2011-2022 走看看