*点击

[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}$  ,$3$ 出现的位置是 $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\n",ans);//输出 
    }
    return 0;//终于没忘打 return 0; 
} 

 

 

 

 

posted @ 2020-08-25 23:05  木偶人-怪咖  阅读(187)  评论(0编辑  收藏  举报
*访客位置3D地图 *目录