BZOJ:2038: [2009国家集训队]小Z的袜子(hose)(莫队算法模板)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038

解题心得:

  • 第一次接触莫队算法,很神奇,很巧妙。莫队算法主要就是用来解决多次询问时维护区间内的信息。维护区间信息第一个想法肯定是线段树,但是线段树维护的区间信息量很少(如最大值,最小值,SUM,GCD等),接触过尺取法的人都知道有时候在处理区间询问的时候可以先将区间排序然后再尺取。莫队算法也用了类似的思维,先对询问区间进行排序,这里排序就用了分块的思想,并不是严格的按照左端点,或者右端点排序,更多的是按照分的块进行排序,这样就减少了维护区间的大幅度乱跳动。
  • 莫队的复杂度分析,每个块长度unit为sqrt(n),如果l端点不在同一个块内需要移动到另一个块内,移动次数最多为2*unit,l在一个块内,r所在位置无法确定,但是排序后可以保证是单调递增的,所以最多移动n次,总共有n/unit个块,最坏要移动n/unit个块,所以总的最坏的复杂度是O(n * sqrt(n) + n * n /sqrt(n) )
  • 这个题来说算概率还是简单的 (Sum(每种袜子个数 2 ) - (r - l + 1)) / ((r - l + 1) * (r - l)),分母可以O(1)内得到,分子可以使用莫队维护

 

 

 

/**************************************************************
    Problem: 2038
    User: swust5120164059
    Language: C++
    Result: Accepted
    Time:3404 ms
    Memory:7560 kb
****************************************************************/

//
//      ┏┛ ┻━━━━━┛ ┻┓
//      ┃       ┃
//      ┃   ━   ┃
//      ┃ ┳┛   ┗┳ ┃
//      ┃       ┃
//      ┃   ┻   ┃
//      ┃       ┃
//      ┗━┓   ┏━━━┛
//        ┃   ┃   神兽保佑
//        ┃   ┃   代码无BUG!
//        ┃   ┗━━━━━━━━━┓
//        ┃           ┣┓
//        ┃             ┏┛
//        ┗━┓ ┓ ┏━━━┳ ┓ ┏━┛
//          ┃ ┫ ┫   ┃ ┫ ┫
//          ┗━┻━┛   ┗━┻━┛
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <math.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;

struct NODE {
    ll l, r, A, B, pos;
}q[maxn];

ll n, m, color[maxn], B[maxn], cnt_color[maxn];

void init() {
    scanf("%lld%lld",&n,&m);
    ll div = (ll) sqrt(n);
    for(int i=1;i<=n;i++) {
        scanf("%lld", &color[i]);
        B[i] = i/div + 1;
    }
    for(int i=0;i<m;i++) {
        scanf("%lld%lld",&q[i].l, &q[i].r);
        q[i].pos = i;
    }
}

bool cmp1(NODE a, NODE b) {
    if(B[a.l] == B[b.l])
        return a.r < b.r;
    return a.l < b.l;
}

bool cmp2(NODE a, NODE b) {
    return a.pos < b.pos;
}

ll ans = 0;
void add(ll pos, ll va) {
    ans -= pow(cnt_color[color[pos]], 2);
    cnt_color[color[pos]] += va;
    ans += pow(cnt_color[color[pos]], 2);
}

int main() {
    init();
    sort(q, q+m, cmp1);
    int l = 1, r = 0;
    for(int i=0;i<m;i++) {
        //左右端点跳转
        while(l < q[i].l)
            add(l++, -1);
        while(l > q[i].l)
            add(--l, 1);
        while(r < q[i].r)
            add(++r, 1);
        while(r > q[i].r)
            add(r--, -1);

        //只有一个袜子概率为0
        if(q[i].l == q[i].r) {
            q[i].A = 0;
            q[i].B = 1;
            continue;
        }
        q[i].A = ans - (q[i].r - q[i].l + 1);
        q[i].B = (q[i].r - q[i].l + 1) * (q[i].r - q[i].l);
    }
    sort(q, q+m, cmp2);
    for(int i=0;i<m;i++) {
        ll GCD = __gcd(q[i].A, q[i].B);
        printf("%lld/%lld\n", q[i].A/GCD, q[i].B/GCD);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/GoldenFingers/p/9428584.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值