Codeforces 86D. Powerful array (MO's Algorithm(区间查询的离线优化))
2016-03-09 15:04
531 查看
题目链接: http://codeforces.com/problemset/problem/86/D
题意:给一个长度为n的数组(1 <= n <= 200000),有 t 个区间询问(1 <= t <= 200000),
每个询问要求指定区间的数对应的一个值,这个值 = 对区间内出现的每个值v,累加 V 乘上(V的出现次数的平方)。
刚刚学了MO's Algorithm,看文章说这题比较经典,就来做做看了。
MO's Algorithm:就是对于一个区间的询问来说,如果该询问支持Add和Remove(即,区间信息的维护支持加和减),就可以考虑使用这个算法。
假设有N个数,M个区间询问,该算法的时间复杂度是O((M+N)*sqrt(N))。
首先来说条件,这个询问必须支持Add和Remove,比如本题就支持。
举个不支持的例子,假设知道[2..5]这个区间的最大值和下标为5的数的数值,是没办法知道[2..4]这个区间的最大值的。
也就是说,最大值只能Add,不能Remove,于是求区间最大值不能用MO's Algorithm。
MO's Algorithm将所有的区间按照L值分成sqrt(n)组,同一组内按照R值来排序。
这样,维护全局变量L,R,以及[L,R]对应的答案RANS (组间要清空维护的这个答案,Clear()函数清空)。
依次改变L和R使之等于每个询问,然后记录下这个询问的答案即可。
这样做了之后,考虑按照L分成的第一组,第一组内的R值已经排了序,所以R是一直向右移动的,于是R最多移动次数为n。
一共sqrt(n)组,所以只考虑R的移动的话,时间复杂度O(n*sqrt(n))。
再来考虑L,每组内的L都在同一个sqrt(n)的范围内,所以每次询问,L最多移动sqrt(n)次(跨组的时候可能会达到2sqrt(n))。
有 t 个询问所以一共大概是 t * sqrt(n)+sqrt(n)次,时间复杂度O( t *sqrt(n))。
所以总复杂度O(( t +n)*sqrt(n))。
总的来说,MO's Algorithm只需3步:
1.区间排序
2.写好Add和Remove和Clear。
3.依次处理各个区间得到答案
处理区间的时候,我以为我是在优化,其实反而降低了速度。以下两份代码只有上面第三步不一样。
低速代码如下:
快速:
题意:给一个长度为n的数组(1 <= n <= 200000),有 t 个区间询问(1 <= t <= 200000),
每个询问要求指定区间的数对应的一个值,这个值 = 对区间内出现的每个值v,累加 V 乘上(V的出现次数的平方)。
刚刚学了MO's Algorithm,看文章说这题比较经典,就来做做看了。
MO's Algorithm:就是对于一个区间的询问来说,如果该询问支持Add和Remove(即,区间信息的维护支持加和减),就可以考虑使用这个算法。
假设有N个数,M个区间询问,该算法的时间复杂度是O((M+N)*sqrt(N))。
首先来说条件,这个询问必须支持Add和Remove,比如本题就支持。
举个不支持的例子,假设知道[2..5]这个区间的最大值和下标为5的数的数值,是没办法知道[2..4]这个区间的最大值的。
也就是说,最大值只能Add,不能Remove,于是求区间最大值不能用MO's Algorithm。
MO's Algorithm将所有的区间按照L值分成sqrt(n)组,同一组内按照R值来排序。
这样,维护全局变量L,R,以及[L,R]对应的答案RANS (组间要清空维护的这个答案,Clear()函数清空)。
依次改变L和R使之等于每个询问,然后记录下这个询问的答案即可。
这样做了之后,考虑按照L分成的第一组,第一组内的R值已经排了序,所以R是一直向右移动的,于是R最多移动次数为n。
一共sqrt(n)组,所以只考虑R的移动的话,时间复杂度O(n*sqrt(n))。
再来考虑L,每组内的L都在同一个sqrt(n)的范围内,所以每次询问,L最多移动sqrt(n)次(跨组的时候可能会达到2sqrt(n))。
有 t 个询问所以一共大概是 t * sqrt(n)+sqrt(n)次,时间复杂度O( t *sqrt(n))。
所以总复杂度O(( t +n)*sqrt(n))。
总的来说,MO's Algorithm只需3步:
1.区间排序
2.写好Add和Remove和Clear。
3.依次处理各个区间得到答案
处理区间的时候,我以为我是在优化,其实反而降低了速度。以下两份代码只有上面第三步不一样。
低速代码如下:
/* 2400 ms 8700 KB */ #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #define maxn 200001 #define LL long long using namespace std; //题目数据 int n,t,p; int a[maxn]; //Mo's Algotithm LL ans[maxn]; struct Interval{ int L,R,id; bool operator <(const Interval &B)const{ return L/p < B.L/p || L/p == B.L/p && R < B.R; } }I[maxn]; LL RANS; int Num[1000001]; void Clear(){memset(Num,0,sizeof(Num));RANS=0;} void Add(LL v){RANS+=((++Num[v]<<1)-1)*v;} void Remove(LL v){RANS-=((--Num[v]<<1)+1)*v;} int main(void) { while(~scanf("%d%d",&n,&t)){ p = sqrt(n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=t;++i) scanf("%d%d",&I[I[i].id=i].L,&I[i].R); //区间排序 sort(I+1,I+t+1); //计算 int L,R=n+1;//[L,R] for(int i=1;i<=t;++i){ if(R > I[i].R){//换区清盘 Clear(); L=1;R=0; } //修改边界 while(R < I[i].R) Add(a[++R]); while(L < I[i].L) Remove(a[L++]); while(L > I[i].L) Add(a[--L]); //记录答案 ans[I[i].id]=RANS; } for(int i=1;i<=t;++i) printf("%I64d\n",ans[i]); } return 0; }
快速:
/* 1996 ms 8600 KB */ #include <iostream> #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #define maxn 200001 #define LL long long using namespace std; //题目数据 int n,t,p; int a[maxn]; //Mo's Algotithm LL ans[maxn]; struct Interval{ int L,R,id; bool operator <(const Interval &B)const{ return L/p < B.L/p || L/p == B.L/p && R < B.R; } }I[maxn]; LL RANS; int Num[1000001]; void Clear(){memset(Num,0,sizeof(Num));RANS=0;} void Add(LL v){RANS+=((++Num[v]<<1)-1)*v;} void Remove(LL v){RANS-=((--Num[v]<<1)+1)*v;} int main(void) { while(~scanf("%d%d",&n,&t)){ p = sqrt(n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=t;++i) scanf("%d%d",&I[I[i].id=i].L,&I[i].R); //区间排序 sort(I+1,I+t+1); Clear(); //计算 int L=1,R=0;//[L,R] for(int i=1;i<=t;++i){ //修改边界 while(R > I[i].R) Remove(a[R--]); while(R < I[i].R) Add(a[++R]); while(L < I[i].L) Remove(a[L++]); while(L > I[i].L) Add(a[--L]); //记录答案 ans[I[i].id]=RANS; } for(int i=1;i<=t;++i) printf("%I64d\n",ans[i]); } return 0; }
相关文章推荐
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- C#数据结构之顺序表(SeqList)实例详解
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 数据结构之Treap详解
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解