P2709-小B的询问-(莫队算法)
莫队算法被称为优雅的暴力,是一种 毒瘤暴力的 区间操作算法。初学莫队,记录一下思想。
对于多个区间查询[l,r]这类问题,可以离线操作,常规做法还有对左区间或者右区间从小到大排序,让左指针或者右指针只走一遍,有效降低时间复杂度。但是遇到[1,1000000],[2,3],[3,1000000],[4,5],[5,1000000]这样的查询区间,虽然可以保证左区间只扫一遍,但是右指针需要从头到尾来回扫,时间复杂度一下子就上去了。莫队算法就是专门针对这种毒瘤题目的。
它的主要思想是分块,对区间的范围n开根号得到一个d,每一个查询区间[l,r]的左区间l除以d,区间看作是全局的第x块,不同块按的区间块从小到大排序,同一块的按右区间从小到大排序(不同块看左,同块看右)。分块排序写一个自定义比较函数就可以了。时间复杂度n*sqrt(n),具体证明看其他大佬博客吧。
对于查询的区间[l,r],指针移动操作比较常规,无非是左指针和右指针左右移动,[5,7]的答案可以由[5,8]或者[5,6]移动一次右指针解决,也可以由[4,7]或[6,7]移动一次左指针解决。对于区间查询的贡献,减去原来的,加上现在的。
题意看了很久才懂,解释一下数据,1到4出现了2个1,对答案贡献2*2,出现1个2,对答案贡献1*1,出现1个3,对答案贡献1*1,加起来就是6。
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<math.h> #include<string> #include<map> #include<queue> #include<stack> #include<set> #include<ctime> #define ll long long #define inf 0x3f3f3f3f ///填充无限小-0x7f using namespace std; struct node { int l; int d; int r; int idx; ll ans; }; node b[50005]; int a[50005]; int num[50005]; int n,m,k; int d; bool cmp1(node p1,node p2)///分块,对左区间开根号,左边不在同一块按左区间小到大排序,左边在同一块按右边大小排序 { if(p1.d<p2.d) return true; else if(p1.d==p2.d) return p1.r<p2.r; else return false; } bool cmp2(node p1,node p2)///离线操作后恢复答案次序 { return p1.idx<p2.idx; } int main()///P2709 莫队入门 { scanf("%d%d%d",&n,&m,&k); d=sqrt(n);///开根号的分块标准 for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) { int l,r; scanf("%d%d",&l,&r); b[i].l=l; b[i].d=l/d;///分块 b[i].r=r; b[i].idx=i; } sort(b+1,b+m+1,cmp1); int L=1,R=1; num[ a[1] ]=1; ll ans=1; for(int i=1;i<=m;i++) { while(L>b[i].l)///左指针往左 { L--; ans=ans-num[ a[L] ]*num[ a[L] ];///减去原来的计数贡献 num[ a[L] ]++; ans=ans+num[ a[L] ]*num[ a[L] ];///加上新的计数贡献 } while(L<b[i].l)///左指针往右 { ans=ans-num[ a[L] ]*num[ a[L] ]; num[ a[L] ]--; ans=ans+num[ a[L] ]*num[ a[L] ]; L++; } while(R<b[i].r)///右指针往右 { R++; ans=ans-num[ a[R] ]*num[ a[R] ]; num[ a[R] ]++; ans=ans+num[ a[R] ]*num[ a[R] ]; } while(R>b[i].r)///右指针往左 { ans=ans-num[ a[R] ]*num[ a[R] ]; num[ a[R] ]--; ans=ans+num[ a[R] ]*num[ a[R] ]; R--; } b[i].ans=ans; } sort(b+1,b+m+1,cmp2); for(int i=1;i<=m;i++) printf("%lld\n",b[i].ans); return 0; }