莫队 + 带修莫队

莫队其实就是一个带优化的暴力,通过将区间询问按一定规则进行排序,从而优化过程,求出答案。

 

 

举一例子:(例子不具备权威性,只是让读者了解莫队是干啥的)

 

/*
输入四个区间

1  4     初始条件,L= R = 0,     将R遍历到4    需要走4步   L走一步,共5次   
4  8   继承上一次 L 和 R 的值,L从1到4   需要3次,R从4到8,需4次,  总计8次     
2  9   同理继承,   L回退2次,  R前进一次                        总计3次      
1  2   同理,L回退1次,R回退7次                                 总计8次                          


如果直接暴力,计算机将要计算     5+8+3+8=24次


如果将询问进行排序呢?假设排列成如下模样:

1 2              总走3次
1 4           总走2次
2 9           总走6次
4 8            总走3次  


全局总走3+2+6+3=14次,这相差是不是很大?这也是莫队的思想,将暴力优化。   
*/

 

 

 

至于如何将询问排序,证明就有一点烦恼,直接说结论:

可以借助分块思想,假设总区间为n,那么每一块区间长度应趋近于    n^(2.0/3)

可以将询问中的  L或者是 R,按其所在块的编号,  从小到大排序,然后再慢慢暴力。。。这就是莫队,说一下莫队的数据极限是     5e5,超过1e7,则需要另谋高就。

 

 

 

 

附上一题:P1494 [国家集训队]小Z的袜子

 

对于该题,有着优化操作,也是组合公式的直接约分简化。

方案1

    

 

 

方案2:

 

 

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<queue>
#include<stack>
#include<map> 
#include<algorithm>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
#define Mem0(x) memset(x,0,sizeof(x))
#define Mem1(x) memset(x,-1,sizeof(x))
#define MemX(x) memset(x,0x3f,sizeof(x))
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f;
const double pi=acos(-1.0);


const int N=500010;
int B,ans[N][5],fz,fm,len,n,m,col[N],cnt[N];
struct s{
	int l,r,id;
	bool operator < (s&b)
	{
		return l/B==b.l/B?r<b.r:l<b.l;
	}
}q[N];    

void init()
{
	len=fz=fm=0;
	memset(cnt,0,sizeof(cnt));
	cin>>n>>m;		B=sqrt(n);
	for (int i=1;i<=n;i++){
		cin>>col[i];
	}
	
	for (int i=0;i<m;i++){
		cin>>q[i].l>>q[i].r;
		q[i].id=i;
	}
	sort(q,q+m);
}
bool cmp(s&a,s&b)
{
	if (a.l/B==b.l/B)
		return a.l>b.l;
}
void add(int x)
{
	fz+=cnt[x];
	cnt[x]++;
	fm+=len;
	len++;
}
void delect(int x)
{
	cnt[x]--;
	fz-=cnt[x];
	len--;
	fm-=len;
}
int main()
{
	init(); 
	int l=1,r=0;
	for (int i=0;i<m;i++){
		if (q[i].l==q[i].r){
			ans[q[i].id][0]=0;
			ans[q[i].id][1]=1;
			continue;
		}
		while (l<q[i].l){
			delect(col[l++]);
		}
		while (l>q[i].l){
			add(col[--l]);
		}
		while (r<q[i].r){
			add(col[++r]);
		}
		while (r>q[i].r){
			delect(col[r--]);
		}
		int tmp=__gcd(fz,fm);
		ans[q[i].id][0]=fz/tmp;
		ans[q[i].id][1]=fm/tmp;
	}
	for (int i=0;i<m;i++){
		printf("%d/%d\n",ans[i][0],ans[i][1]);
	}
	return 0;
}

  

 

带修莫队就是带上了修改。。。。    (待更新)

 

 

*****************************************更新******************************************

 简单的来说:

  带修莫队  =  莫队    + 时间轴  

对于原来莫队,就是先对  询问 L按所在分块编号从小到大排序,当相等的时候则按 R 所在分块的从大到小排序。

至于带修莫队,就是在原莫队上再加一轴(时间轴),先将l分块,再讲r分块,同一块的按 t 排序。

    排好序之后,查询遍历,如果当前修改次数 比 本次查询改的多,则改回去,反之则再次修改,直到修改次数与本次查询改的次数相同。

 

posted @ 2019-08-05 16:51  生活待我如初恋  阅读(420)  评论(0编辑  收藏  举报