Description

给定一个长度为 \(n\) 的序列,求有多少子区间满足区间众数严格大于区间长度的一半。如果区间有多个出现次数最多且不同的数则取较小的数为众数。

Limitation

对于全部的数据,\(1 \leq n \leq 500000\)

序列中数的值域为 \([0,n)\)

子任务:序列中的数值域为 \([0,7]\)

Solution

考虑如果区间有多个出现次数最多且不同的数,那么这个区间显然是不合法的。于是区间出现多个众数取最小的限制其实没有什么 * 用。

考虑枚举区间众数是 \(x\),将区间中等于 \(x\) 的数置为 \(1\),不等于的置为 \(-1\),考虑 \(x\) 是该区间的众数且出现次数严格大于区间长度一半的充要条件是区间的和大于 \(0\)。

于是考虑问题被转化成了给定一个仅含有 \(-1\) 和 \(1\) 的序列,求有多少子区间满足区间和大于 \(0\)。

对于序列的子区间问题的一个经典套路是通过做前缀和转化成序列点对问题,这是因为子区间有 \(O(n^2)\) 个,而点只有 \(O(n)\) 个,子区间甚至遍历一遍复杂度都难以支持,但是单点只需要在遍历时维护对应信息即可。

考虑对序列做前缀和,那么 \([l,~r]\) 区间是合法的当且仅当 \(sum_r - sum_{l - 1} > 0\),也即 \(sum_r > sum_{l - 1}\)。于是只需要求前缀和序列的顺序对个数即可。

对于序列值域较小的子任务,可以直接枚举众数,然后每次用权值线段树/权值树状数组 \(O(n \log n)\) 去求顺序对个数。时间复杂度 \(O(\max_{i = 1}^{n}\{A_i\} \times n \log n)\)。

考虑枚举这 \(O(n)\) 个众数一共产生 \(O(n)\) 个 \(1,~-1\) 序列,共有 \(O(n^2)\) 个数,在这 \(O(n^2)\) 个数中只有 \(O(n)\) 个 \(1\),剩下全是 \(-1\)。由于可以认为是这 \(O(n)\) 个 \(1\) 和 \(O(n)\) 个空白(即两个相邻序列)将这 \(O(n^2)\) 个 \(-1\) 隔开,于是连续的 \(-1\) 序列有 \(O(n)\) 个。

考虑每段连续的 \(-1\) 序列,假设它们的前缀和值域是 \([l,~r]\),则它们对答案产生的贡献是

\[\sum_{i = l}^{r} \sum_{j = -n}^{i - 1} tree_i~=~(r - l + 1) \times \sum_{i = -n}^{l-1} tree_i + \sum_{i = l}^{r} tree_i \times (r - i)~=~(r - l + 1) \times \sum_{i = -n}^{l-1} tree_i + r\sum_{i = l}^{r} tree_i - \sum_{i = l}^{r} tree_i \times i
\]

其中 \(tree_i\) 为 \(i\) 出现的次数。

于是考虑只需要用权值线段树维护 \(tree_i\) 和 \(tree_i \times i\) 即可。考虑将这连续一段 \(-1\) 插入可以区间 \(+1\)。对于不是 \(-1\) 的位置,显然可以单次 \(O(\log n)\) 计算。于是总复杂度 \(O(n \log n)\)。

Code

#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define printf(...) typedef long long int ll; namespace IPT {
const int L = 1000000;
char buf[L], *front=buf, *end=buf;
char GetChar() {
if (front == end) {
end = buf + fread(front = buf, 1, L, stdin);
if (front == end) return -1;
}
return *(front++);
}
} template <typename T>
inline void qr(T &x) {
char ch = IPT::GetChar(), lst = ' ';
while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
if (lst == '-') x = -x;
} namespace OPT {
char buf[120];
} template <typename T>
inline void qw(T x, const char aft, const bool pt) {
if (x < 0) {x = -x, putchar('-');}
int top=0;
do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
while (top) putchar(OPT::buf[top--]);
if (pt) putchar(aft);
} const int maxn = 500005; int n;
ll ans;
int MU[maxn];
std::vector<int>num[maxn]; struct Tree {
int l, r;
ll sum;
Tree *ls, *rs;
std::pair<ll, ll> v;
std::pair<int, bool> tag; Tree(const int _l, const int _r) : l(_l), r(_r), sum(((r - l + 1ll) * (r + l)) >> 1), ls(NULL), rs(NULL) {} inline void maketag(std::pair<int, bool> v) {
if (v.second) {
this->v.first = this->v.second = this->tag.first = 0;
this->tag.second = true;
}
this->v.first += (r - l + 1ll) * v.first;
this->v.second += v.first * sum;
this->tag.first += v.first;
} inline void pushdown() {
this->ls->maketag(this->tag);
this->rs->maketag(this->tag);
this->tag.first = this->tag.second = 0;
} inline void pushup() {
this->v.first = this->ls->v.first + this->rs->v.first;
this->v.second = this->ls->v.second + this->rs->v.second;
} inline bool InRange(const int l, const int r) {
return (this->l >= l) && (this->r <= r);
} inline bool OutofRange(const int l, const int r) {
return (this->l > r) || (this->r < l);
}
};
Tree *rot; void build(Tree *const u);
int calc(const int k, const int p);
void update(Tree *const u, const int l, const int r);
std::pair<ll, ll> query(Tree *const u, const int l, const int r); int main() {
freopen("1.in", "r", stdin);
qr(n); qr(ans); ans = 0;
for (int i = 1; i <= n; ++i) {
qr(MU[i]); num[MU[i]].push_back(i);
}
build(rot = new Tree(-n, n));
for (int k = 0; k < n; ++k) {
rot->maketag(std::make_pair(0, true));
int l = 0, cnt = 0;
for (auto u : num[k]) {
if (u != l) {
int r = u - 1;
int ar = calc(cnt, l), al = calc(cnt, r);
auto ret1 = query(rot, -n, al - 1), ret2 = query(rot, al, ar);
ans += ret1.first * (ar - al + 1) + ret2.first * ar - ret2.second;
update(rot, al, ar);
printf("QWAQ%d %d %lld\n", k, u, ans);
}
int v = calc(++cnt, u);
ans += query(rot, -n, v - 1).first;
printf("QWAQ%d %d %lld\n", k, u, ans);
update(rot, v, v);
l = u + 1;
}
if (l <= n) {
int r = n;
int ar = calc(cnt, l), al = calc(cnt, r);
auto ret1 = query(rot, -n, al - 1), ret2 = query(rot, al, ar);
printf("EMM%lld %lld %lld %lld\n", ret1.first, ret1.second, ret2.first, ret2.second);
ans += ret1.first * (ar - al + 1) + ret2.first * ar - ret2.second;
}
printf("QWQ%d %lld\n", k, ans);
}
qw(ans, '\n', true);
return 0;
} void update(Tree *const u, const int l, const int r) {
if (u->InRange(l, r)) {
u->maketag(std::make_pair(1, false));
} else if (!u->OutofRange(l, r)) {
u->pushdown();
update(u->ls, l, r); update(u->rs, l, r);
u->pushup();
}
printf("UPD%d %d %d %d %d %d\n", u->l, u->r, l, r, u->v.first, u->v.second);
} std::pair<ll, ll> query(Tree *const u, const int l, const int r) {
printf("EMMQAQ%d %d %d %d %d\n", u->l, u->r, l, r, u->v.first);
if (u->InRange(l, r)) {
return u->v;
} else if (u->OutofRange(l, r)) {
return std::make_pair(0ll, 0ll);
} else {
u->pushdown();
auto rl = query(u->ls, l, r), rr = query(u->rs, l, r);
return std::make_pair(rl.first + rr.first, rl.second + rr.second);
u->pushup();
}
} inline int calc(const int k, const int p) {
return (k << 1) - p;
} void build(Tree *const u) {
if (u->l == u->r) return;
int mid = (u->l + u->r) >> 1;
build(u->ls = new Tree(u->l, mid)); build(u->rs = new Tree(mid + 1, u->r));
}

Summary

对于子区间问题,可以通过做前缀和转化成点对问题。

【线段树】【P4062】 [Code+#1]Yazid 的新生舞会的更多相关文章

  1. luogu P4062 [Code+#1]Yazid 的新生舞会(线段树+套路)

    今天原来是平安夜啊 感觉这题是道好题. 一个套路枚举权值\(x\),把权值等于\(x\)的设为1,不等于的设为-1,然后问题转化为多少个区间权值和大于. 发现并不是很好做,还有一个套路,用前缀和查分来 ...

  2. 洛谷 P4062 - [Code+#1]Yazid 的新生舞会(权值线段树)

    题面传送门 题意: 给出一个序列 \(a\),求 \(a\) 有多少个子区间 \([l,r]\),满足这个区间中出现次数最多的数出现次数 \(>\dfrac{r-l+1}{2}\) \(1 \l ...

  3. 洛谷 P4062 - [Code+#1]Yazid 的新生舞会 的线性做法

    洛谷题面传送门 一个线性做法. \(n\log n\) 解法可以戳这里查看 首先回顾一下 \(n\log n\) 解法的过程:我们对于每一个数 \(x\),考察其出现位置,设为 \(t_1,t_2,t ...

  4. P4062 [Code+#1]Yazid 的新生舞会

    思路:分治 提交:2次 错因:数组开小 题解: 我们枚举一下众数\(x\). 设\(s[n]=\sum_{i=1}^n [a[i]==x]\) 那么对于区间\((l,r]\),有\(s[r]-s[l] ...

  5. [题解] [Code+#1]Yazid 的新生舞会

    题面 题解 upd : \(cnt_i\) 代表值为 \(i\) 的个数 我们可以暴力枚举众数 \(k\) 把等于 \(k\) 的赋值成 1 , 不等于 \(k\) 的赋值成 -1 这样原序列就变成了 ...

  6. 【BZOJ5110】[CodePlus2017]Yazid 的新生舞会 线段树

    [BZOJ5110][CodePlus2017]Yazid 的新生舞会 Description Yazid有一个长度为n的序列A,下标从1至n.显然地,这个序列共有n(n+1)/2个子区间.对于任意一 ...

  7. BZOJ.5110.[CodePlus2017]Yazid 的新生舞会(线段树/树状数组/分治)

    LOJ BZOJ 洛谷 又来发良心题解啦 \(Description\) 给定一个序列\(A_i\).求有多少个子区间,满足该区间众数出现次数大于区间长度的一半. \(n\leq5\times10^5 ...

  8. 「CodePlus 2017 11 月赛」Yazid 的新生舞会(树状数组/线段树)

    学习了新姿势..(一直看不懂大爷的代码卡了好久T T 首先数字范围那么小可以考虑枚举众数来计算答案,设当前枚举到$x$,$s_i$为前$i$个数中$x$的出现次数,则满足$2*s_r-r > 2 ...

  9. [loj 6253] Yazid的新生舞会

    (很久之前刷的题现在看起来十分陌生a) 题意: 给你一个长度为n的序列A,定义一个区间$[l,r]$是“新生舞会的”当且仅当该区间的众数次数严格大于$\frac{r-l+1}{2}$,求有多少子区间是 ...

随机推荐

  1. 第八节:EF Core连接MySql数据库

    一. 前提 1.安装EF Core连接MySQL的驱动,这里有两类: (1).Oracle官方出品:MySql.Data.EntityFrameworkCore (版本:8.0.17) (2).其他第 ...

  2. [Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column 'information_schema.PROFILING.SEQ'

    在Navicat Premium中执行Mysql的一条删除语句,虽然执行成功了,却提示已下错误: 受影响的行: 时间: .005s of ORDER BY clause is not in GROUP ...

  3. 『Norma 分治』

    Norma Description Input Format 第1行,一个整数N: 第2~n+1行,每行一个整数表示序列a. Output Format 输出答案对10^9取模后的结果. Sample ...

  4. Linux进程启动/指令执行方式研究

    1. 通过glibc api执行系统指令 0x1:system() glibc api system是linux系统提供的函数调用之一,glibc也提供了对应的封装api. system函数的原型为: ...

  5. 【LeetCode】145. Binary Tree Postorder Traversal

    Difficulty: Hard  More:[目录]LeetCode Java实现 Description https://leetcode.com/problems/binary-tree-pos ...

  6. NetCoreApi框架搭建(二、Nlog使用配置)

    本文只配置了简单文件存储 1.添加nuget包 2.添加日志配置文件nlog.config 这里配置了三个target区分不同的日志,具体配置需要自己研究,推荐链接https://www.cnblog ...

  7. 在IE中点击转跳,并打开chorme浏览器继续浏览指定页面,IE自定义ocx控件开发

    因项目需要,需要开发一个功能:在IE中点击转跳,并打开chorme浏览器继续浏览指定页面. 分析需求后,参考了: https://www.cnblogs.com/ffjiang/p/7908025.h ...

  8. Python学习笔记之replace()

    10-2 可使用方法replace()将字符串中的特定单词都替换为另一个单词. 读取你刚创建的文件learning_python.txt 中的每一行,将其中的Python 都替换为另一门语言的名称,如 ...

  9. 开发--CentOS-7安装及配置

    开发|CentOS-7安装及配置 本文主要进行详细讲解CentOS7.5系统的安装过程,以及CentOS系统初始化技术.我并不想将这篇文章变成一个教程,尽管我将详细的进行每一步的讲解,enjoy! 前 ...

  10. 10. Javascript 前后端数据加密

    为了加强项目的接口安全程度,需求如下 var options = { // 前端需要传送的数据加密 data: { abc: 123, bcd: 123, cds: '撒旦教付货款12313', }, ...