莫队算法

来源:互联网 发布:三国演义电视剧 知乎 编辑:程序博客网 时间:2024/05/17 01:07

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

对于询问

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;}


原创粉丝点击