离线算法相关

莫队算法

莫队算法由莫涛提出,可以解决一类区间询问问题。

普通莫队

对于序列上的区间询问,如果询问取件能在 O ( 1 ) O(1) O(1) 的时间复杂度内扩展到 [ l − 1 , r ] [l-1,r] [l1,r] [ l , r − 1 ] [l,r-1] [l,r1] [ l + 1 , r ] [l+1,r] [l+1,r] [ l , r + 1 ] [l,r+1] [l,r+1],那么可以利用普通莫队在 O ( n n ) O(n\sqrt n) O(nn ) 的时间复杂度内解决所有区间询问。莫队算法的应用基础就是需要保证在 O ( 1 ) O(1) O(1) 的时间复杂度内移动相邻区间。

首先利用分块的思想,把原序列分成 n \sqrt n n 个块,把每一个块编号,对于所有查询,我们考虑按照左端点 l l l 所在的块的编号进行排序,如果左端点 l l l 所在的块编号相同再按照右端点 r r r 的大小排序。排序之后再进行左右指针的移动操作可以大大优化时间复杂度。对于块长的选择,据数据结构大神 lxl 说块长定为 n 2 3 m \dfrac{n}{\sqrt{\dfrac{2}{3}m}} 32m n 更快( n n n 是序列长度, m m m 是询问次数)。

莫队算法的精髓就是左右指针的跳跃,举个栗子,如下图所示:

离线算法相关_第1张图片

现在我们已经知道了区间和 Q 1 Q_1 Q1,要求区间和 Q 2 Q_2 Q2,考虑通过移动左右指针暴力统计答案。我们只需要右移右指针,在移动的过程中不断加上经过数值的值,然后右移左指针,在移动的过程中不断减去经过数值的值。需要注意的是,莫队算法中的左右指针移动顺序很有讲究,建议手推,同时在考场上写挂之后可以先不用样例测试,而是自己用脑子跑一遍。总而言之就是一定要先扩展区间再去缩小区间。

时间复杂度为 O ( n n ) O(n\sqrt n) O(nn ),证明略。

莫队算法的基本操作如下:

void update(int p/*pos*/,int sig/*sign*/){}//更新答案的操作

void solve()
{
    for(int i=1;i<=m;i++)
    {
        node now=q[i];
        while(l>now.l) --l,update(l,-1);
        while(r<now.r) ++r,update(r,1);
        while(l<now.l) ++l,update(l,1);
        while(r>now.r) --r,update(r,-1);
        ans[now.id]=nans;
    }
}

练手板子题,代码如下:

#include 
using namespace std;
#define int long long

const int maxn=50005;
int len,a[maxn],ans[maxn],cnt[maxn],nans,n,m,k,l=1,r=0/*l为上次的左端点,r为上次的右端点*/;

struct node
{
	int l,r,id,num/*分块编号*/;
}q[maxn];

bool cmp(node a,node b)
{
	if(a.num!=b.num) return a.num<b.num;
	return a.r<b.r;
}

void add(int pos)
{nans+=2*cnt[a[pos]]+1,cnt[a[pos]]++;}

void del(int pos)
{nans-=2*cnt[a[pos]]-1,cnt[a[pos]]--;}

void solve()
{
	for(int i=1;i<=m;i++)
    {
        node now=q[i];
        while(l>now.l) --l,add(l);
        while(r<now.r) ++r,add(r);
        while(l<now.l) del(l),++l;
        while(r>now.r) del(r),--r;
        ans[now.id]=nans;
    }
}

signed main()
{
	cin>>n>>m>>k;
	len=sqrt(n);
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++)
		cin>>q[i].l>>q[i].r,q[i].id=i,q[i].num=(q[i].l-1)/len+1;
	sort(q+1,q+m+1,cmp);
	solve();
	for(int i=1;i<=m;i++) cout<<ans[i]<<endl;
	return 0;
}

你可能感兴趣的:(#,线性数据结构,算法,c++)