莫队算法

莫队算法其实就是暴力分块,举个形象的例子,如果你要在大海里找一个东西,你是会一会儿在南半球找,然后一会儿又到北半球找呢,还是先在南半球找完了,再在北半球找。(这里假设物体是不动的)很明显是先在北半球找后再在南半球找或者相反。莫队就是这样的原理,主要是减少了转换区域时的损耗。

对于询问

1.先以起点排序,放在各个桶内,桶是sqrt(n)那么大的,然后每个桶内以终点排序。

然后查询的时候是每个桶从小到大,你会发现起点是在桶里乱动的(这就好比你在南半球乱找)消耗是sqrt(n)的,桶只有sqrt(n)那么大。

2.然后发现终点是渐渐增大的(终点最多移动n次嘛)这就相当于你从南半球渐渐的往北半球找。这里是o(n)的

3.然后可能还换桶,这里的复杂度还是sqrt(n)的

所以对于每次询问复杂度是sqrt(n)的。当然如果配合上树状数组什么的复杂度就要乘一个log了比如这道题。


对于此题主要是要注意ins的作用还有就是给每个询问一个id因为排序后再输出的话已经不是原来的顺序了。

这题可能还能用可持久化做吧,速度大概能达到200000ms左右吧如果姿势不对的话,姿势对的话估计就是10000ms左右

还有如果你有什么东西掉海里了,那是不用找的。。。。。。。。。。。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
using namespace std;
struct query
{
	int l, r,a,b,num;
};
query q[1000021];
int sum1[100020], sum2[100020], n, belong[100020],m,a[100020],isin[100020];
int ans1[1000021], ans2[1000021];
bool com(query a, query b)
{
	if (belong[a.l] == belong[b.l])return a.r < b.r;
	return belong[a.l] < belong[b.l];
}
void add1(int pos,int kind)
{
	while (pos <= n)
	{
		sum1[pos]+=kind; pos += pos&(-pos);
	}
}
void add2(int pos,int kind)
{
	while (pos <= n)
	{
		sum2[pos]+=kind; pos += pos&(-pos);
	}
}
int fsum(int l)
{
	int ans = 0;
	while (l)
	{
		ans += sum1[l]; l -= l&(-l);
	}
	return ans;
}
int ssum(int l)
{
	int ans = 0;
	while (l)
	{
		ans += sum2[l]; l -= l&(-l);
	}
	return ans;
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <=n; i++)
		scanf("%d", &a[i]);
	int len = sqrt((double)n) + 1;
	for (int i = 1; i <= n; i++)belong[i] = i%len == 0 ? (i / len) : (i / len + 1);
	for (int i = 0; i < m; i++)
		scanf("%d%d%d%d", &q[i].l, &q[i].r, &q[i].a, &q[i].b), q[i].num = i;
	sort(q, q + m, com);
	int l = 1; int r = 0;
	for (int i = 0; i < m; i++)
	{
		while (l < q[i].l)
		{
			add1(a[l], -1);
			isin[a[l]]--;
			if (!isin[a[l]])add2(a[l], -1);
			l++;
		}
		while (l > q[i].l)
		{
			l--;
			add1(a[l], 1); 
			if (!isin[a[l]]) add2(a[l], 1); 
			isin[a[l]]++;
		}
		while (r < q[i].r)
		{
			r++;
			add1(a[r], 1); 
			if (!isin[a[r]]) add2(a[r], 1); 
			isin[a[r]]++;
		}
		while (r > q[i].r)
		{

			add1(a[r], -1);
			isin[a[r]]--; 
			if(!isin[a[r]])add2(a[r], -1);
			r--;
		}
		ans1[q[i].num] = fsum(q[i].b)-fsum(q[i].a-1);
		ans2[q[i].num]= ssum(q[i].b) - ssum(q[i].a - 1);
	}
	for (int i = 0; i < m; i++)
		printf("%d %d\n", ans1[i], ans2[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值