「2009国家集训队」小Z的袜子 - 普通莫队

题意

有一个长度为 \(n\) 的序列 \(\{c_i\}\) 。现在给出 \(m\) 个询问,每次给出两个数 \(l,r\) ,从编号在 \(l\)\(r\) 之间的数中随机选出两个不同的数,求两个数相等的概率。

思路

莫队算法模板题

对于区间 \([l,r]\),以 \(l\) 所在块的编号为第一关键字, \(r\) 为第二关键字从小到大排序。

然后从序列的第一个询问开始计算答案,第一个询问通过直接暴力算出,复杂度为 \(O(n\sqrt{n})\) ,后面的询问在前一个询问的基础上得到答案。

#include <cmath>
#include <cstdio>
#include <algorithm>

#define ll long long
#define bi(a) ((a - 1) / sqn + 1)

typedef struct {
  ll l, r, i;
} Node;

const int maxn = 5e4 + 10;

Node q[maxn];

ll n, m, sqn, arr[maxn], l, r, ans, col[maxn], sub[maxn], mot[maxn];

bool cmp(Node a, Node b) {
  if (bi(a.l) == bi(b.l)) return a.r < b.r;
  return a.l < b.l;
}

ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }

int main() {
  scanf("%lld %lld", &n, &m); sqn = std::sqrt(n);
  for (int i = 1; i <= n; ++ i) scanf("%lld", arr + i);
  for (int i = 1; i <= m; ++ i) {
    scanf("%lld %lld", &q[i].l, &q[i].r); q[i].i = i;
  }
  std::sort(q + 1, q + m + 1, cmp); l = r = q[1].l, col[arr[l]] ++; // obviously
  for (int i = 1, gcdnum; i <= m; ++ i) {
    for (; l < q[i].l; l++) col[arr[l]]--, ans -= col[arr[l]];
    for (--l; l >= q[i].l; l--) ans += col[arr[l]], col[arr[l]]++;
    for (; r > q[i].r; r--) col[arr[r]]--, ans -= col[arr[r]];
    for (++r; r <= q[i].r; r++) ans += col[arr[r]], col[arr[r]]++;
    sub[q[i].i] = ans, l = q[i].l, r = q[i].r;
    mot[q[i].i] = 1ll * ((r - l) * (r - l + 1)) >> 1;
  }
  for (int i = 1; i <= m; ++ i) {
    ll gcdn = gcd(sub[i], mot[i]);
    printf("%lld/%lld\n", sub[i] / gcdn, mot[i] / gcdn);
  } 
}

转载于:https://www.cnblogs.com/alessandrochen/p/11484984.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值