这次比赛有一道题卡了很久,之前以为是Lucas,没想到是莫队算法,早就听说莫队算法对于离线处理时间复杂度一流,这次也算长了见识,这是一道莫队亲手出的莫队算法题,很正规了
挂一个很棒的博客 : 莫队算法学习链接
莫队算法时间复杂度之所以低,是因为提前对数据进行了预处理,使得重复的计算少了很多,值得一提的就是分块,把一个大的区间分成了好多小块。。。这里称之为分块思想,利用优化枚举,双关键字排序,成功的降低了时间复杂度,这个博客里有详细介绍
具体到这道题,细节也不少,比如运用1LL*将int类型数据转化为long long 类型来防止数据溢出,对于分子的更新的推导(博客末尾也有解答),感觉很不错,继续学习
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
using namespace std;
#define maxn 50005
int n, m, col[maxn], unit, qu[maxn];
long long sum[maxn], ans = 0;
struct node
{
int l, r, id;
long long A, B;
}a[maxn];
long long gcd(long long a, long long b)
{
return b == 0 ? a : gcd(b, a % b);
}
bool cmp1(node a, node b)
{
return qu[a.l] == qu[b.l] ? a.r < b.r : a.l < b.l;
}
bool cmp2(node a, node b)
{
return a.id < b.id;
}
void update(int x, int add)
{
ans -= sum[col[x]]*sum[col[x]];
sum[col[x]] += add;
ans += sum[col[x]]*sum[col[x]];
}
int main()
{
scanf("%d%d",&n,&m);
unit = sqrt(n);
for(int i = 1; i <= n; i ++)
{
scanf("%d",&col[i]);
qu[i] = i / unit + 1;
}
for(int i = 1; i <= m; i ++)
{
scanf("%d%d",&a[i].l,&a[i].r);
a[i].id = i;
}
sort(a + 1, a + 1 + m, cmp1);
int l = 1, r = 0;
for(int i = 1; i <= m; i ++)
{
while(l < a[i].l){update(l, -1); l ++;}
while(l > a[i].l){update(l - 1, 1); l --;}
while(r > a[i].r){update(r, -1); r --;}
while(r < a[i].r){update(r + 1, 1); r ++;}
if(a[i].l == a[i].r)
{
a[i].A = 0;
a[i].B = 1;
continue;
}
a[i].A = ans - (a[i].r - a[i].l + 1);
a[i].B = 1LL * (a[i].r - a[i].l + 1) * (a[i].r - a[i].l);//1LL*先把int类型变量换成long long型,防止溢出
long long k = gcd(a[i].A, a[i].B);
a[i].A /= k;
a[i].B /= k;
}
sort(a + 1, a + 1 + m, cmp2);
for(int i = 1; i <= m; i ++)
{
printf("%lld/%lld\n",a[i].A,a[i].B);
}
return 0;
}