小Z的袜子(莫队算法)

题目链接 :小Z的袜子

这次比赛有一道题卡了很久,之前以为是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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值