[BZOJ3781][P2709]小B的询问[莫队]

入门题

对于一个区间的询问,如果在已知\([l,r]\)的答案时可以用O(1)的时间求出左右端点\(±1\)的答案,就可以使用莫队来优化。

设已知区间为\([l_1,r_1]\),所求区间为\([l_2,r_2]\)
可知求得\([l_2,r_2]\)的成本是\(|l_1-l_2| + |r_1-r_2|\)如果把这两个区间看成点,这个成本就是两点的曼哈顿距离
对于多个询问,求出曼哈顿距离最小生成树就可以以最小成本获得答案,使用一种奇怪的方式 ——sort来获得最优的转移方式 我并不知道原理

对于整个区间进行分块,每个询问按照块编号为第一关键字,右端点为第二关键字进行排序,然后依次处理每个询问即可。看起来还是很暴力

卡常技巧我仍然不知道为什么:块大小\(\frac{n}{\sqrt{m*\frac{2}{3}}}\)比\(\sqrt n\)更快,sort时对于同一块里的询问按照奇偶排序更快
效果图

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 50005; typedef long long LL;
struct Data{
  int l, r, id, pos;
  inline bool operator < (const Data & rhs) const {
    return pos ^ rhs.pos ? pos < rhs.pos : pos&1?r<rhs.r:r>rhs.r;
    // if (pos == rhs.pos) return r < rhs.r;
    // return pos < rhs.pos;
  }
}d[MAXN];

int b[MAXN], a[MAXN], n, m, k, cnt[MAXN];LL anss[MAXN];

int main(void) {
  scanf("%d%d%d", &n, &m, &k);
  int block = n/sqrt(m*2/3);
  for(int i = 1; i <= n; ++i) scanf("%d", a+i);
  for(int i = 1; i <= m; ++i) {
    scanf("%d%d", &d[i].l, &d[i].r); d[i].id = i;
    d[i].pos = (d[i].l-1)/block + 1;
  }
  sort(d+1, d+m+1);
  int l = 1, r = 0; LL ans = 0;
  for(int i = 1; i <= m; ++i) {
    while (l > d[i].l) l--, cnt[a[l]]++, ans += 2*cnt[a[l]] - 1;
    while (r < d[i].r) r++, cnt[a[r]]++, ans += 2*cnt[a[r]] - 1;
    while (l < d[i].l) cnt[a[l]]--, ans -= 2*cnt[a[l]] + 1, l++;
    while (r > d[i].r) cnt[a[r]]--, ans -= 2*cnt[a[r]] + 1, r--;
    anss[d[i].id] = ans;
  }
  for(int i = 1; i <= m; ++i) printf("%lld\n", anss[i]);
  return 0;
}

原文地址:https://www.cnblogs.com/Ycrpro/p/8761440.html

时间: 2024-11-03 22:03:52

[BZOJ3781][P2709]小B的询问[莫队]的相关文章

P2709 小B的询问 莫队

这个题,莫队很容易想到(格式很明显),然后直接用数学公式维护一下和就行了. 题干: 题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第

P2709 小B的询问 莫队算法

题意:小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 就是求区间不同数字个数的平方和 注意平方数可以拆开从1-n 递推... #include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<

luoguP2709 小B的询问 [莫队]

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3

小B的询问 莫队分块

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 复制 6 4 3 1 3 2 1 1

BZOJ 3781 小B的询问 莫队算法

题目大意:一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数. 思路:莫队走起. CODE: #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 50010 using namespac

Bzoj 3781: 小B的询问 莫队,分块,暴力

3781: 小B的询问 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 426  Solved: 284[Submit][Status][Discuss] Description 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. Input 第一行,三个整数N.M.K. 第二行

P2709 小B的询问 【普通莫队】

这是我的莫队入门题,我也了解到了莫队分为普通莫队以及带修莫队.顾名思义,普通莫队不需要修改区间的值,而带修莫队处理区间的值会修改的查询. 能用莫队的前提条件: 1.在知道 [l, r]中信息时,可以在 O(1)的复杂度内知道 [l - 1, r],[l + 1, r],[l, r - 1],[l, r + 1]的信息,否则复杂度会爆炸. 2.能离线处理. 莫队的时间复杂度为O(n ^ 1.5),但实际效果一定会优于这个时间复杂度. 莫队的主要操作: 1.对查询区间,以 left 来进行分块,然后

清橙A1206 小Z的袜子(莫队算法)

A1206. 小Z的袜子 时间限制:1.0s   内存限制:512.0MB 总提交次数:744   AC次数:210   平均分:44.44 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2010中国国家集训队命题答辩 问题描述 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是

洛谷 P2709 小B的询问

https://www.luogu.org/problem/show?pid=2709 题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表